diff --git a/.gn b/.gn index ea58dc547c..aea8f2c650 100644 --- a/.gn +++ b/.gn @@ -24,7 +24,12 @@ secondary_source = "//build/secondary/" # matching these patterns (see "gn help label_pattern" for format) will not have # their includes checked for proper dependencies when you run either # "gn check" or "gn gen --check". -no_check_targets = [ "//third_party/icu/*" ] +no_check_targets = [ + "//third_party/icu/*", + + # TODO(crbug.com/1151236) Remove once fixed. + "//base/allocator/partition_allocator:partition_alloc", +] # These are the list of GN files that run exec_script. This whitelist exists # to force additional review for new uses of exec_script, which is strongly @@ -60,4 +65,7 @@ default_args = { # Differently from Chromium, WebRTC still support SDK 21. default_min_sdk_version = 21 + + # Prevent jsoncpp to pass -Wno-deprecated-declarations to users + jsoncpp_no_deprecated_declarations = false } diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..634f3a6ac5 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Tommi Tomas Gunnarsson diff --git a/.vpython b/.vpython index a57f82ff3e..d226875f02 100644 --- a/.vpython +++ b/.vpython @@ -31,7 +31,7 @@ wheel: < version: "version:5.2.2" > -# Used by tools_webrtc/perf/webrtc_dashboard_upload.py. +# Used by tools_webrtc/perf/process_perf_results.py. wheel: < name: "infra/python/wheels/httplib2-py2_py3" version: "version:0.10.3" diff --git a/.vpython3 b/.vpython3 index 99b1a0d8e9..39d735dcd1 100644 --- a/.vpython3 +++ b/.vpython3 @@ -31,7 +31,7 @@ wheel: < version: "version:5.8.0.chromium.2" > -# Used by tools_webrtc/perf/webrtc_dashboard_upload.py. +# Used by tools_webrtc/perf/process_perf_results.py. wheel: < name: "infra/python/wheels/httplib2-py3" version: "version:0.19.1" diff --git a/AUTHORS b/AUTHORS index e4729a574b..68653f4e0d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ CZ Theng Danail Kirov Dave Cowart David Porter +David Sanders Dax Booysen Dennis Angelo Dharmesh Chauhan @@ -43,6 +44,7 @@ Dirk-Jan C. Binnema Dmitry Lizin Eike Rathke Eric Rescorla, RTFM Inc. +Filip Hlasek Frederik Riedel, Frogg GmbH Giji Gangadharan Graham Yoakum @@ -80,6 +82,7 @@ Mike Gilbert Min Wang Mo Zanaty Niek van der Maas +Olivier CrĂȘte Pali Rohar Paul Kapustin Peng Yu @@ -114,10 +117,12 @@ Victor Costan Vladimir Beloborodov Xiaohong Xu Xiaolei Yu +Xinchao Tian Yaowen Guo Yura Yaroshevich Yuriy Pavlyshak Yusuke Suzuki +Pengfei Han # END individuals section. # BEGIN organizations section. @@ -143,6 +148,7 @@ NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> Optical Tone Ltd <*@opticaltone.com> Pengutronix e.K. <*@pengutronix.de> +Raptor Computing Systems, LLC <*@raptorcs.com> RingCentral, Inc. <*@ringcentral.com> Signal Messenger, LLC <*@signal.org> Sinch AB <*@sinch.com> diff --git a/BUILD.gn b/BUILD.gn index fa4fcace49..66b3c52ec2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -66,6 +66,7 @@ if (!build_with_chromium) { "net/dcsctp:dcsctp_unittests", "pc:peerconnection_unittests", "pc:rtc_pc_unittests", + "pc:slow_peer_connection_unittests", "rtc_tools:rtp_generator", "rtc_tools:video_replay", "stats:rtc_stats_unittests", @@ -81,6 +82,13 @@ if (!build_with_chromium) { # see bugs.webrtc.org/11027#c5. deps += [ ":webrtc_lib_link_test" ] } + if (is_ios) { + deps += [ + "examples:apprtcmobile_tests", + "sdk:sdk_framework_unittests", + "sdk:sdk_unittests", + ] + } if (is_android) { deps += [ "examples:android_examples_junit_tests", @@ -92,7 +100,7 @@ if (!build_with_chromium) { } if (rtc_enable_protobuf) { deps += [ - "audio:low_bandwidth_audio_test", + "audio:low_bandwidth_audio_perf_test", "logging:rtc_event_log_rtp_dump", "tools_webrtc/perf:webrtc_dashboard_upload", ] @@ -466,9 +474,7 @@ if (!build_with_chromium) { "modules/video_capture:video_capture_internal_impl", "p2p:rtc_p2p", "pc:libjingle_peerconnection", - "pc:peerconnection", "pc:rtc_pc", - "pc:rtc_pc_base", "rtc_base", "sdk", "video", @@ -567,6 +573,7 @@ if (rtc_include_tests && !build_with_chromium) { "rtc_base/task_utils:pending_task_safety_flag_unittests", "rtc_base/task_utils:repeating_task_unittests", "rtc_base/task_utils:to_queued_task_unittests", + "rtc_base/time:timestamp_extrapolator_unittests", "rtc_base/units:units_unittests", "sdk:sdk_tests", "test:rtp_test_utils", @@ -637,7 +644,12 @@ if (rtc_include_tests && !build_with_chromium) { ] data = video_engine_tests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] shard_timeout = 900 } if (is_ios) { @@ -682,7 +694,12 @@ if (rtc_include_tests && !build_with_chromium) { data = webrtc_perf_tests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] shard_timeout = 4500 } if (is_ios) { @@ -713,6 +730,23 @@ if (rtc_include_tests && !build_with_chromium) { } } +# Build target for standalone dcsctp +rtc_static_library("dcsctp") { + # Only the root target should depend on this. + visibility = [ "//:default" ] + sources = [] + complete_static_lib = true + suppressed_configs += [ "//build/config/compiler:thin_archive" ] + defines = [] + deps = [ + "net/dcsctp/public:factory", + "net/dcsctp/public:socket", + "net/dcsctp/public:types", + "net/dcsctp/socket:dcsctp_socket", + "net/dcsctp/timer:task_queue_timeout", + ] +} + # ---- Poisons ---- # # Here is one empty dummy target for each poison type (needed because diff --git a/DEPS b/DEPS index 9bc9237a85..b7b90a7405 100644 --- a/DEPS +++ b/DEPS @@ -10,43 +10,43 @@ vars = { # chromium waterfalls. More info at: crbug.com/570091. 'checkout_configuration': 'default', 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"', - 'chromium_revision': '1d65e161b823702fbaed6c1e74bf01008cad2edf', + 'chromium_revision': 'bca1174cd507f2c0c1eef2d4943cf861be09c772', # Keep the Chromium default of generating location tags. 'generate_location_tags': True, # ResultDB version - 'resultdb_version': 'git_revision:735a8a662d3874d8b1d795a40e46ea0f57b52758', + 'resultdb_version': 'git_revision:6cc18e2763e180929d70c786b419c1f8e6bcc66c', } deps = { # TODO(kjellander): Move this to be Android-only once the libevent dependency # in base/third_party/libevent is solved. 'src/base': - 'https://chromium.googlesource.com/chromium/src/base@5de2454055b98b7cedb8b3ca31a5e73fd7f2329d', + 'https://chromium.googlesource.com/chromium/src/base@86e89d2f5c7618cdf8b10946802d2c24160bc9d5', 'src/build': - 'https://chromium.googlesource.com/chromium/src/build@bcaab68639cce04f2c52c6d65f5199af6d763d75', + 'https://chromium.googlesource.com/chromium/src/build@08c3a4a86e6cff1335d06ef481e41f129fa3f6ff', 'src/buildtools': - 'https://chromium.googlesource.com/chromium/src/buildtools@2a745cc87d28e53996469189bc7661811d6f9c34', + 'https://chromium.googlesource.com/chromium/src/buildtools@7208eddba161d85108097c3c5975264c04e3cad8', # Gradle 6.6.1. Used for testing Android Studio project generation for WebRTC. 'src/examples/androidtests/third_party/gradle': { 'url': 'https://chromium.googlesource.com/external/github.com/gradle/gradle.git@f2d1fb54a951d8b11d25748e4711bec8d128d7e3', 'condition': 'checkout_android', }, 'src/ios': { - 'url': 'https://chromium.googlesource.com/chromium/src/ios@9adcdab41620a7c899745609e4083d023e449d1e', + 'url': 'https://chromium.googlesource.com/chromium/src/ios@9774f8152da1a93583d340cdd588e78575ca37a7', 'condition': 'checkout_ios', }, 'src/testing': - 'https://chromium.googlesource.com/chromium/src/testing@67a0c97ff4b57f30d97825107bee9e112a020f79', + 'https://chromium.googlesource.com/chromium/src/testing@947e3a4e8a4892eb0e59b1c9d53929ae000191eb', 'src/third_party': - 'https://chromium.googlesource.com/chromium/src/third_party@f30eafbf5286fc58f85215253b7807c2b7125361', + 'https://chromium.googlesource.com/chromium/src/third_party@6a087b22ff656342208fbe97202722606c235ea3', 'src/buildtools/linux64': { 'packages': [ { 'package': 'gn/gn/linux-amd64', - 'version': 'git_revision:0725d7827575b239594fbc8fd5192873a1d62f44', + 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', } ], 'dep_type': 'cipd', @@ -56,7 +56,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/mac-${{arch}}', - 'version': 'git_revision:0725d7827575b239594fbc8fd5192873a1d62f44', + 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', } ], 'dep_type': 'cipd', @@ -66,7 +66,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/windows-amd64', - 'version': 'git_revision:0725d7827575b239594fbc8fd5192873a1d62f44', + 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', } ], 'dep_type': 'cipd', @@ -78,20 +78,9 @@ deps = { 'src/buildtools/third_party/libc++/trunk': 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@79a2e924d96e2fc1e4b937c42efd08898fa472d7', 'src/buildtools/third_party/libc++abi/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@77c52e2f68f76dbe661420d85f51521805990ae2', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@bb1e46cb72b89a8bb61cc16ad33267b53889aae3', 'src/buildtools/third_party/libunwind/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@b86911d6e7455c4be2ddbc390803aecad19cfc3a', - - 'src/tools/clang/dsymutil': { - 'packages': [ - { - 'package': 'chromium/llvm-build-tools/dsymutil', - 'version': 'M56jPzDv1620Rnm__jTMYS62Zi8rxHVq7yw0qeBFEgkC', - } - ], - 'condition': 'checkout_mac', - 'dep_type': 'cipd', - }, + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@3e0d3ec5851c065a401dfe1bb5161da6a1edb192', 'src/third_party/android_system_sdk': { 'packages': [ @@ -117,7 +106,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/aapt2', - 'version': '0yR8wK_fSMgdVKwnx4nRPi-amaLV7Kcr4Os6mg_DGI4C', + 'version': 'kZqQH92bSO1p0a7_hcrana_9YjtSBU1te7TEtNVBoCUC', }, ], 'condition': 'checkout_android', @@ -128,7 +117,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/bundletool', - 'version': '2ZcLVDxyRwp8FzpeYLtLT0TfSRweZxvwh1-Kx1jZ_FoC', + 'version': 'AqsPZpWJh-ZyGraHKlbH8XgjRnmyDmolX4HhwPEo9XUC', }, ], 'condition': 'checkout_android', @@ -136,11 +125,11 @@ deps = { }, 'src/third_party/boringssl/src': - 'https://boringssl.googlesource.com/boringssl.git@123eaaef26abc278f53ae338e9c758eb01c70b08', + 'https://boringssl.googlesource.com/boringssl.git@227ff6e6425283b83594a91a1aa81cc78f1a88df', 'src/third_party/breakpad/breakpad': - 'https://chromium.googlesource.com/breakpad/breakpad.git@08bd844599bf04c71707e8f59a8013a941264695', + 'https://chromium.googlesource.com/breakpad/breakpad.git@8b68c72a3fff2bb687c7f411e5c1c09e356b8603', 'src/third_party/catapult': - 'https://chromium.googlesource.com/catapult.git@25f38be662aec0c8b1509024001e5cc5254a363c', + 'https://chromium.googlesource.com/catapult.git@e9b55266586c1188837e156587ff75221ed18120', 'src/third_party/ced/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/compact_enc_det.git@ba412eaaacd3186085babcd901679a48863c7dd5', }, @@ -149,22 +138,25 @@ deps = { 'src/third_party/crc32c/src': 'https://chromium.googlesource.com/external/github.com/google/crc32c.git@fa5ade41ee480003d9c5af6f43567ba22e4e17e6', 'src/third_party/depot_tools': - 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@1b93e14c92416185694f8764be425f82fa0d4242', + 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@9997ceb9a1c8680b029e85dc9fe7515dec23cf69', 'src/third_party/ffmpeg': - 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@574c39cce3231c69bc9a02ac475c27d944bdb113', + 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@e481fc655a6287e657a88e8c2bcd6f411d254d70', 'src/third_party/findbugs': { 'url': 'https://chromium.googlesource.com/chromium/deps/findbugs.git@4275d9ac8610db6b1bc9a5e887f97e41b33fac67', 'condition': 'checkout_android', }, + 'src/third_party/grpc/src': { + 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@ee2b75e33740d1a88c0e2aeec1b14435e17a889e', + }, # Used for embedded builds. CrOS & Linux use the system version. 'src/third_party/fontconfig/src': { 'url': 'https://chromium.googlesource.com/external/fontconfig.git@452be8125f0e2a18a7dfef469e05d19374d36307', 'condition': 'checkout_linux', }, 'src/third_party/freetype/src': - 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@24db55ecb81ca726b9c7e12e37d54a986c84f014', + 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@5d49473f8579d7f5f687d3fe52af977468f8e090', 'src/third_party/harfbuzz-ng/src': - 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@0acf466c44143de2e9b9cc0375cb25ec67cb132f', + 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@6454cec085ba51cefcd12b1f8027bc4a647347d5', 'src/third_party/google_benchmark/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@f730846b0a3c0dc0699978846fb14ffb2fad0bdc', }, @@ -182,9 +174,9 @@ deps = { 'dep_type': 'cipd', }, 'src/third_party/googletest/src': - 'https://chromium.googlesource.com/external/github.com/google/googletest.git@43efa0a4efd40c78b9210d15373112081899a97c', + 'https://chromium.googlesource.com/external/github.com/google/googletest.git@af29db7ec28d6df1c7f0f745186884091e602e07', 'src/third_party/icu': { - 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@2e0f2989441ec2f55abec30f48e89981dbac2c34', + 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@585942f33d939a11f4600bd5042649b7ca189008', }, 'src/third_party/jdk': { 'packages': [ @@ -207,7 +199,7 @@ deps = { 'dep_type': 'cipd', }, 'src/third_party/jsoncpp/source': - 'https://chromium.googlesource.com/external/github.com/open-source-parsers/jsoncpp.git@9059f5cad030ba11d37818847443a53918c327b1', # from svn 248 + 'https://chromium.googlesource.com/external/github.com/open-source-parsers/jsoncpp.git@42e892d96e47b1f6e29844cc705e148ec4856448', # from svn 248 'src/third_party/junit/src': { 'url': 'https://chromium.googlesource.com/external/junit.git@64155f8a9babcfcf4263cf4d08253a1556e75481', 'condition': 'checkout_android', @@ -216,23 +208,23 @@ deps = { 'src/third_party/libFuzzer/src': 'https://chromium.googlesource.com/chromium/llvm-project/compiler-rt/lib/fuzzer.git@debe7d2d1982e540fbd6bd78604bf001753f9e74', 'src/third_party/libjpeg_turbo': - 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@02959c3ee17abacfd1339ec22ea93301292ffd56', + 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@22f1a22c99e9dde8cd3c72ead333f425c5a7aa77', 'src/third_party/libsrtp': 'https://chromium.googlesource.com/chromium/deps/libsrtp.git@5b7c744eb8310250ccc534f3f86a2015b3887a0a', 'src/third_party/dav1d/libdav1d': - 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@cce2b0564d3b0aa1a5e4ec0c561b6a1b8497cebb', + 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@87f9a81cd770e49394a45deca7a3df41243de00b', 'src/third_party/libaom/source/libaom': - 'https://aomedia.googlesource.com/aom.git@c9feb209a41343981c215072583d385bdda2bab1', + 'https://aomedia.googlesource.com/aom.git@ef14518388c0a41c1d3b992f75d5886c9da33832', 'src/third_party/libunwindstack': { - 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@6868358481bb1e5e20d155c1084dc436c88b5e6b', + 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@3c86843ae0f8d560ae0d15b92e34ce88cf83057a', 'condition': 'checkout_android', }, 'src/third_party/perfetto': - 'https://android.googlesource.com/platform/external/perfetto.git@605bd3a3ad96c56f98780e7b4b6b247e1d9e0ae9', + 'https://android.googlesource.com/platform/external/perfetto.git@b3694fd90110c28532e47831bd1ffc517d0ccc1b', 'src/third_party/libvpx/source/libvpx': - 'https://chromium.googlesource.com/webm/libvpx.git@51415c4076578d3cbc32fcd0d683161c3e887814', + 'https://chromium.googlesource.com/webm/libvpx.git@cb1abee1455ac7e552da271ac64c71d117caaa77', 'src/third_party/libyuv': - 'https://chromium.googlesource.com/libyuv/libyuv.git@b4ddbaf549a1bf5572bf703fd2862d1eb7380c6a', + 'https://chromium.googlesource.com/libyuv/libyuv.git@d62ee21e6627888e84466b5a5ed15775582ac67b', 'src/third_party/lss': { 'url': 'https://chromium.googlesource.com/linux-syscall-support.git@92a65a8f5d705d1928874420c8d0d15bde8c89e5', 'condition': 'checkout_android or checkout_linux', @@ -248,12 +240,12 @@ deps = { }, 'src/third_party/openh264/src': - 'https://chromium.googlesource.com/external/github.com/cisco/openh264@b52786888ddce9d6bc06b7825ba9bffc65924e0c', + 'https://chromium.googlesource.com/external/github.com/cisco/openh264@fac04ceb3e966f613ed17e98178e9d690280bba6', 'src/third_party/r8': { 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'cEv1yyfxfmP_MaZrG22cR7YPc7hehgHAZd82lRx0DFAC', + 'version': 'ovozeRSDDfERnEFpDo_WS6OYOcEF7oT1JzGxCSf-g0kC', }, ], 'condition': 'checkout_android', @@ -277,15 +269,13 @@ deps = { 'url': 'https://chromium.googlesource.com/chromium/third_party/ub-uiautomator.git@00270549ce3161ae72ceb24712618ea28b4f9434', 'condition': 'checkout_android', }, - 'src/third_party/usrsctp/usrsctplib': - 'https://chromium.googlesource.com/external/github.com/sctplab/usrsctp@62d7d0c928c9a040dce96aa2f16c00e7e67d59cb', # Dependency used by libjpeg-turbo. 'src/third_party/yasm/binaries': { 'url': 'https://chromium.googlesource.com/chromium/deps/yasm/binaries.git@52f9b3f4b0aa06da24ef8b123058bb61ee468881', 'condition': 'checkout_win', }, 'src/tools': - 'https://chromium.googlesource.com/chromium/src/tools@55c693b555dff2de2756ae4cd2e1322737879a6f', + 'https://chromium.googlesource.com/chromium/src/tools@685f07470702cdbe83796519133de9f29f1c046b', 'src/third_party/accessibility_test_framework': { 'packages': [ @@ -309,17 +299,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/bazel': { - 'packages': [ - { - 'package': 'chromium/third_party/bazel', - 'version': 'VjMsf48QUWw8n7XtJP2AuSjIGmbQeYdWdwyxVvIRLmAC', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - 'src/third_party/bouncycastle': { 'packages': [ { @@ -376,7 +355,7 @@ deps = { }, 'src/third_party/android_ndk': { - 'url': 'https://chromium.googlesource.com/android_ndk.git@9644104c8cf85bf1bdce5b1c0691e9778572c3f8', + 'url': 'https://chromium.googlesource.com/android_ndk.git@8388a2be5421311dc75c5f937aae13d821a27f3d', 'condition': 'checkout_android', }, @@ -384,7 +363,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'EqXyh_ypMvWmNBJPk_Xk9bp-pc8lerSz48cySEsnbMwC', + 'version': '_6LSpTMrwppYMyP7cQHuhfab0hq8aJFpArKyFPpd9wgC', }, ], 'condition': 'checkout_android', @@ -401,10 +380,6 @@ deps = { 'package': 'chromium/third_party/android_sdk/public/emulator', 'version': 'gMHhUuoQRKfxr-MBn3fNNXZtkAVXtOwMwT7kfx8jkIgC', }, - { - 'package': 'chromium/third_party/android_sdk/public/extras', - 'version': 'ppQ4TnqDvBHQ3lXx5KPq97egzF5X2FFyOrVHkGmiTMQC', - }, { 'package': 'chromium/third_party/android_sdk/public/patcher', 'version': 'I6FNMhrXlpB-E1lOhMlvld7xt9lBVNOO83KIluXDyA0C', @@ -423,7 +398,7 @@ deps = { }, { 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', - 'version': 'Ez2NWws2SJYCF6qw2O-mSCqK6424l3ZdSTpppLyVR_cC', + 'version': 'PGPmqJtSIQ84If155ba7iTU846h5WJ-bL5d_OoUWEWYC', }, ], 'condition': 'checkout_android', @@ -456,7 +431,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/robolectric', - 'version': 'Kdd6dNFAKqj9g9Bsfo2z1zQr52Vk60EL_wb9Bf2c8rcC', + 'version': 'WZ96VJuhBM63xzHb-_E72Tf46M9yIbfia6basI1YG4EC', }, ], 'condition': 'checkout_android', @@ -478,7 +453,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/turbine', - 'version': 'ZweGxnankyMPNqORbYcApzrOXpyg-9XcK7_-qdjELlcC', + 'version': 'FJ-IOPRGQsHUZwVeYmVw_idRk5mUUP6_Uj2i6mKQlEMC', }, ], 'condition': 'checkout_android', @@ -489,30 +464,16 @@ deps = { 'packages': [ { 'package': 'infra/tools/luci/isolate/${{platform}}', - 'version': 'git_revision:462d0a9cdbe947cd652fcd0c54f64ebc712858a5', + 'version': 'git_revision:2aa3d7e5e8662c5193059a490f07b7d91331933e', }, { 'package': 'infra/tools/luci/swarming/${{platform}}', - 'version': 'git_revision:462d0a9cdbe947cd652fcd0c54f64ebc712858a5', + 'version': 'git_revision:2aa3d7e5e8662c5193059a490f07b7d91331933e', }, ], 'dep_type': 'cipd', }, - # TODO(crbug.com/1184780) Move this back to ANDROID_DEPS Generated Code - # section once org_robolectric_shadows_multidex is updated to a new version - # that does not need jetify. - 'src/third_party/android_deps/libs/org_robolectric_shadows_multidex': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_multidex', - 'version': 'version:4.3.1-cr1', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - # Everything coming after this is automatically updated by the auto-roller. # === ANDROID_DEPS Generated Code Start === # Generated by //third_party/android_deps/fetch_all.py @@ -520,7 +481,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_core_common', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -531,7 +492,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_core_runtime', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -542,7 +503,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_common', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -553,7 +514,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_common_java8', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -564,7 +525,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_livedata', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -575,7 +536,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_livedata_core', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -586,7 +547,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_runtime', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -597,29 +558,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/android_arch_lifecycle_viewmodel', - 'version': 'version:2@1.1.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/backport_util_concurrent_backport_util_concurrent': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/backport_util_concurrent_backport_util_concurrent', - 'version': 'version:2@3.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/classworlds_classworlds': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/classworlds_classworlds', - 'version': 'version:2@1.1-alpha-2.cr0', + 'version': 'version:2@1.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -630,7 +569,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_animated_vector_drawable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -641,7 +580,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_appcompat_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -652,7 +591,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_asynclayoutinflater', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -663,7 +602,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_cardview_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -674,7 +613,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_collections', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -685,7 +624,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_coordinatorlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -696,7 +635,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_cursoradapter', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -707,7 +646,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_customview', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -718,7 +657,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_design', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -729,7 +668,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_documentfile', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -740,7 +679,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_drawerlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -751,7 +690,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_interpolator', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -762,7 +701,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_loader', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -773,7 +712,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_localbroadcastmanager', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -784,7 +723,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_multidex', - 'version': 'version:2@1.0.0.cr0', + 'version': 'version:2@1.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -795,7 +734,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_print', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -806,7 +745,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_recyclerview_v7', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -817,7 +756,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_slidingpanelayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -828,7 +767,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_annotations', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -839,7 +778,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_compat', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -850,7 +789,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_core_ui', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -861,7 +800,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_core_utils', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -872,7 +811,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_fragment', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -883,7 +822,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_media_compat', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -894,7 +833,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_v4', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -905,7 +844,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_support_vector_drawable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -916,7 +855,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_swiperefreshlayout', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -927,7 +866,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_transition', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -938,7 +877,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_versionedparcelable', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -949,7 +888,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_support_viewpager', - 'version': 'version:2@28.0.0.cr0', + 'version': 'version:2@28.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -960,7 +899,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_common', - 'version': 'version:2@30.2.0-beta01.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -971,7 +910,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.5.cr1', }, ], 'condition': 'checkout_android', @@ -982,7 +921,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration', - 'version': 'version:2@1.1.1.cr0', + 'version': 'version:2@1.1.5.cr1', }, ], 'condition': 'checkout_android', @@ -993,7 +932,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api', - 'version': 'version:2@30.2.0-beta01.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -1004,7 +943,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_android_tools_sdk_common', - 'version': 'version:2@30.2.0-beta01.cr0', + 'version': 'version:2@30.2.0-beta01.cr1', }, ], 'condition': 'checkout_android', @@ -1015,7 +954,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_github_ben_manes_caffeine_caffeine', - 'version': 'version:2@2.8.8.cr0', + 'version': 'version:2@2.8.8.cr1', }, ], 'condition': 'checkout_android', @@ -1026,7 +965,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_github_kevinstern_software_and_algorithms', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/com_google_android_apps_common_testing_accessibility_framework_accessibility_test_framework', + 'version': 'version:2@3.1.2.cr1', }, ], 'condition': 'checkout_android', @@ -1037,7 +987,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_datatransport_transport_api', - 'version': 'version:2@2.2.1.cr0', + 'version': 'version:2@2.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -1048,7 +998,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@20.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1059,7 +1009,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth_api_phone', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@18.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1070,7 +1020,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_auth_base', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@18.0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1081,7 +1031,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_base', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@18.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1092,7 +1042,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_basement', - 'version': 'version:2@17.5.0.cr0', + 'version': 'version:2@18.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1103,7 +1053,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cast', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1114,7 +1064,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cast_framework', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1125,7 +1075,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_clearcut', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1136,7 +1086,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_cloud_messaging', - 'version': 'version:2@16.0.0.cr0', + 'version': 'version:2@16.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1147,7 +1097,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_fido', - 'version': 'version:2@19.0.0-beta.cr0', + 'version': 'version:2@19.0.0-beta.cr1', }, ], 'condition': 'checkout_android', @@ -1158,7 +1108,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_flags', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1169,7 +1119,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_gcm', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1180,7 +1130,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_iid', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1191,7 +1141,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_instantapps', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@18.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1202,7 +1152,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_location', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@19.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1213,7 +1163,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_phenotype', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1224,7 +1174,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_places_placereport', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1235,7 +1185,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_stats', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1246,7 +1196,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_tasks', - 'version': 'version:2@17.2.0.cr0', + 'version': 'version:2@18.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1257,7 +1207,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_vision', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@20.1.3.cr1', }, ], 'condition': 'checkout_android', @@ -1268,7 +1218,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_vision_common', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@19.1.3.cr1', }, ], 'condition': 'checkout_android', @@ -1279,7 +1229,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_material_material', - 'version': 'version:2@1.6.0-alpha01.cr0', + 'version': 'version:2@1.6.0-alpha01.cr1', }, ], 'condition': 'checkout_android', @@ -1290,7 +1240,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core', - 'version': 'version:2@1.10.0.cr0', + 'version': 'version:2@1.10.0.cr1', }, ], 'condition': 'checkout_android', @@ -1301,7 +1251,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_auto_common', - 'version': 'version:2@1.1.2.cr0', + 'version': 'version:2@1.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -1312,7 +1262,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_service_auto_service', - 'version': 'version:2@1.0-rc6.cr0', + 'version': 'version:2@1.0-rc6.cr1', }, ], 'condition': 'checkout_android', @@ -1323,7 +1273,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_service_auto_service_annotations', - 'version': 'version:2@1.0-rc6.cr0', + 'version': 'version:2@1.0-rc6.cr1', }, ], 'condition': 'checkout_android', @@ -1334,18 +1284,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_auto_value_auto_value_annotations', - 'version': 'version:2@1.7.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/com_google_code_findbugs_jformatstring': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/com_google_code_findbugs_jformatstring', - 'version': 'version:2@3.0.0.cr0', + 'version': 'version:2@1.9.cr1', }, ], 'condition': 'checkout_android', @@ -1356,7 +1295,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_code_findbugs_jsr305', - 'version': 'version:2@3.0.2.cr0', + 'version': 'version:2@3.0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1367,7 +1306,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_code_gson_gson', - 'version': 'version:2@2.8.0.cr0', + 'version': 'version:2@2.8.0.cr1', }, ], 'condition': 'checkout_android', @@ -1378,7 +1317,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1389,7 +1328,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_compiler', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1400,7 +1339,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_producers', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1411,7 +1350,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_dagger_dagger_spi', - 'version': 'version:2@2.30.cr0', + 'version': 'version:2@2.30.cr1', }, ], 'condition': 'checkout_android', @@ -1422,7 +1361,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation', - 'version': 'version:2@2.10.0.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1433,7 +1372,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations', - 'version': 'version:2@2.10.0.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1444,7 +1383,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_check_api', - 'version': 'version:2@2.10.0.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1455,7 +1394,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_core', - 'version': 'version:2@2.10.0.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1466,7 +1405,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations', - 'version': 'version:2@2.10.0.cr0', + 'version': 'version:2@2.11.0.cr1', }, ], 'condition': 'checkout_android', @@ -1477,7 +1416,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_javac', - 'version': 'version:2@9+181-r4173-1.cr0', + 'version': 'version:2@9+181-r4173-1.cr1', }, ], 'condition': 'checkout_android', @@ -1488,7 +1427,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_javac_shaded', - 'version': 'version:2@9-dev-r4023-3.cr0', + 'version': 'version:2@9-dev-r4023-3.cr1', }, ], 'condition': 'checkout_android', @@ -1499,7 +1438,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_annotations', - 'version': 'version:2@16.0.0.cr0', + 'version': 'version:2@16.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1510,7 +1449,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_common', - 'version': 'version:2@19.5.0.cr0', + 'version': 'version:2@19.5.0.cr1', }, ], 'condition': 'checkout_android', @@ -1521,7 +1460,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_components', - 'version': 'version:2@16.1.0.cr0', + 'version': 'version:2@16.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1532,7 +1471,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_encoders', - 'version': 'version:2@16.1.0.cr0', + 'version': 'version:2@16.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1543,7 +1482,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_encoders_json', - 'version': 'version:2@17.1.0.cr0', + 'version': 'version:2@17.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1554,7 +1493,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_iid', - 'version': 'version:2@21.0.1.cr0', + 'version': 'version:2@21.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1565,7 +1504,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_iid_interop', - 'version': 'version:2@17.0.0.cr0', + 'version': 'version:2@17.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1576,7 +1515,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_installations', - 'version': 'version:2@16.3.5.cr0', + 'version': 'version:2@16.3.5.cr1', }, ], 'condition': 'checkout_android', @@ -1587,7 +1526,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_installations_interop', - 'version': 'version:2@16.0.1.cr0', + 'version': 'version:2@16.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1598,7 +1537,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_measurement_connector', - 'version': 'version:2@18.0.0.cr0', + 'version': 'version:2@18.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1609,7 +1548,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_firebase_firebase_messaging', - 'version': 'version:2@21.0.1.cr0', + 'version': 'version:2@21.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1620,7 +1559,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_flatbuffers_flatbuffers_java', - 'version': 'version:2@2.0.3.cr0', + 'version': 'version:2@2.0.3.cr1', }, ], 'condition': 'checkout_android', @@ -1631,7 +1570,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_googlejavaformat_google_java_format', - 'version': 'version:2@1.5.cr0', + 'version': 'version:2@1.5.cr1', }, ], 'condition': 'checkout_android', @@ -1642,7 +1581,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_failureaccess', - 'version': 'version:2@1.0.1.cr0', + 'version': 'version:2@1.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1653,7 +1592,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava', - 'version': 'version:2@31.0-jre.cr0', + 'version': 'version:2@31.0.1-jre.cr1', }, ], 'condition': 'checkout_android', @@ -1664,7 +1603,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava_android', - 'version': 'version:2@31.0-android.cr0', + 'version': 'version:2@31.0-android.cr1', }, ], 'condition': 'checkout_android', @@ -1675,7 +1614,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_listenablefuture', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1686,7 +1625,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations', - 'version': 'version:2@1.3.cr0', + 'version': 'version:2@1.3.cr1', }, ], 'condition': 'checkout_android', @@ -1697,7 +1636,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_java', - 'version': 'version:2@3.4.0.cr0', + 'version': 'version:2@3.19.2.cr1', }, ], 'condition': 'checkout_android', @@ -1708,7 +1647,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_javalite', - 'version': 'version:2@3.19.3.cr0', + 'version': 'version:2@3.19.3.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/com_google_protobuf_protobuf_lite': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_lite', + 'version': 'version:2@3.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1719,7 +1669,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_googlecode_java_diff_utils_diffutils', - 'version': 'version:2@1.3.0.cr0', + 'version': 'version:2@1.3.0.cr1', }, ], 'condition': 'checkout_android', @@ -1730,7 +1680,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_squareup_javapoet', - 'version': 'version:2@1.13.0.cr0', + 'version': 'version:2@1.13.0.cr1', }, ], 'condition': 'checkout_android', @@ -1741,7 +1691,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_squareup_javawriter', - 'version': 'version:2@2.1.1.cr0', + 'version': 'version:2@2.1.1.cr1', }, ], 'condition': 'checkout_android', @@ -1752,7 +1702,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/io_github_java_diff_utils_java_diff_utils', - 'version': 'version:2@4.0.cr0', + 'version': 'version:2@4.0.cr1', }, ], 'condition': 'checkout_android', @@ -1763,7 +1713,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_annotation_javax_annotation_api', - 'version': 'version:2@1.3.2.cr0', + 'version': 'version:2@1.3.2.cr1', }, ], 'condition': 'checkout_android', @@ -1774,7 +1724,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_annotation_jsr250_api', - 'version': 'version:2@1.0.cr0', + 'version': 'version:2@1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1785,29 +1735,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/javax_inject_javax_inject', - 'version': 'version:2@1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/nekohtml_nekohtml': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/nekohtml_nekohtml', - 'version': 'version:2@1.9.6.2.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/nekohtml_xercesminimal': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/nekohtml_xercesminimal', - 'version': 'version:2@1.9.6.2.cr0', + 'version': 'version:2@1.cr1', }, ], 'condition': 'checkout_android', @@ -1818,7 +1746,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/net_ltgt_gradle_incap_incap', - 'version': 'version:2@0.2.cr0', + 'version': 'version:2@0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1829,183 +1757,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/net_sf_kxml_kxml2', - 'version': 'version:2@2.3.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_ant_ant': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_ant_ant', - 'version': 'version:2@1.8.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_ant_ant_launcher': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_ant_ant_launcher', - 'version': 'version:2@1.8.0.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_ant_tasks': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_ant_tasks', - 'version': 'version:2@2.1.3.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_artifact': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_artifact', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_artifact_manager': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_artifact_manager', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_error_diagnostics': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_error_diagnostics', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_model': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_model', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_plugin_registry': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_plugin_registry', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_profile': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_profile', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_project': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_project', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_repository_metadata': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_repository_metadata', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_maven_settings': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_maven_settings', - 'version': 'version:2@2.2.1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_file': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_file', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_lightweight': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_lightweight', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_shared': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_http_shared', - 'version': 'version:2@1.0-beta-6.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_apache_maven_wagon_wagon_provider_api': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_apache_maven_wagon_wagon_provider_api', - 'version': 'version:2@1.0-beta-6.cr0', + 'version': 'version:2@2.3.0.cr1', }, ], 'condition': 'checkout_android', @@ -2016,7 +1768,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup', - 'version': 'version:2@1.2.1.cr0', + 'version': 'version:2@1.2.1.cr1', }, ], 'condition': 'checkout_android', @@ -2027,7 +1779,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual', - 'version': 'version:2@2.5.5.cr0', + 'version': 'version:2@2.5.5.cr1', }, ], 'condition': 'checkout_android', @@ -2038,7 +1790,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual', - 'version': 'version:2@3.12.0.cr0', + 'version': 'version:2@3.12.0.cr1', }, ], 'condition': 'checkout_android', @@ -2049,7 +1801,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone', - 'version': 'version:2@3.15.0.cr0', + 'version': 'version:2@3.15.0.cr1', }, ], 'condition': 'checkout_android', @@ -2060,51 +1812,29 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations', - 'version': 'version:2@1.17.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_container_default': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_container_default', - 'version': 'version:2@1.0-alpha-9-stable-1.cr0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_interpolation': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_interpolation', - 'version': 'version:2@1.11.cr0', + 'version': 'version:2@1.17.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_codehaus_plexus_plexus_utils': { + 'src/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_plexus_plexus_utils', - 'version': 'version:2@1.5.15.cr0', + 'package': 'chromium/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit', + 'version': 'version:2@4.4.1.201607150455-r.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit': { + 'src/third_party/android_deps/libs/org_hamcrest_hamcrest': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit', - 'version': 'version:2@4.4.1.201607150455-r.cr0', + 'package': 'chromium/third_party/android_deps/libs/org_hamcrest_hamcrest', + 'version': 'version:2@2.2.cr1', }, ], 'condition': 'checkout_android', @@ -2115,7 +1845,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_annotations', - 'version': 'version:2@13.0.cr0', + 'version': 'version:2@13.0.cr1', }, ], 'condition': 'checkout_android', @@ -2126,7 +1856,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib', - 'version': 'version:2@1.6.10.cr0', + 'version': 'version:2@1.6.21.cr1', }, ], 'condition': 'checkout_android', @@ -2137,7 +1867,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_common', - 'version': 'version:2@1.6.10.cr0', + 'version': 'version:2@1.6.21.cr1', }, ], 'condition': 'checkout_android', @@ -2148,7 +1878,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk7', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.6.20.cr1', }, ], 'condition': 'checkout_android', @@ -2159,7 +1889,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk8', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.6.20.cr1', }, ], 'condition': 'checkout_android', @@ -2170,7 +1900,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_android', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.6.1.cr1', }, ], 'condition': 'checkout_android', @@ -2181,7 +1911,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm', - 'version': 'version:2@1.5.0.cr0', + 'version': 'version:2@1.6.1.cr1', }, ], 'condition': 'checkout_android', @@ -2192,7 +1922,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_metadata_jvm', - 'version': 'version:2@0.1.0.cr0', + 'version': 'version:2@0.1.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_jsoup_jsoup': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_jsoup_jsoup', + 'version': 'version:2@1.14.3.cr1', }, ], 'condition': 'checkout_android', @@ -2203,7 +1944,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2214,7 +1955,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_analysis', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2225,7 +1966,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_commons', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2236,7 +1977,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_tree', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2247,7 +1988,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_ow2_asm_asm_util', - 'version': 'version:2@7.0.cr0', + 'version': 'version:2@9.2.cr1', }, ], 'condition': 'checkout_android', @@ -2258,7 +1999,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_pcollections_pcollections', - 'version': 'version:2@2.1.2.cr0', + 'version': 'version:2@3.1.4.cr1', }, ], 'condition': 'checkout_android', @@ -2269,7 +2010,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_annotations', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2280,7 +2021,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_junit', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_robolectric_nativeruntime': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_robolectric_nativeruntime', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2291,7 +2043,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_pluginapi', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2302,7 +2054,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_plugins_maven_dependency_resolver', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2313,7 +2065,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_resources', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2324,7 +2076,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_robolectric', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2335,7 +2087,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_sandbox', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2346,7 +2098,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadowapi', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2357,7 +2109,18 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_framework', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_robolectric_shadows_multidex': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_multidex', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2368,7 +2131,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_playservices', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2379,7 +2142,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2390,7 +2153,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils_reflector', - 'version': 'version:2@4.3.1.cr0', + 'version': 'version:2@4.7.3.cr1', }, ], 'condition': 'checkout_android', @@ -2505,12 +2268,40 @@ hooks = [ 'action': ['python3', 'src/build/util/lastchange.py', '-o', 'src/build/util/LASTCHANGE'], }, + # Pull dsymutil binaries using checked-in hashes. + { + 'name': 'dsymutil_mac_arm64', + 'pattern': '.', + 'condition': 'host_os == "mac" and host_cpu == "arm64"', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-browser-clang', + '-s', 'src/tools/clang/dsymutil/bin/dsymutil.arm64.sha1', + '-o', 'src/tools/clang/dsymutil/bin/dsymutil', + ], + }, + { + 'name': 'dsymutil_mac_x64', + 'pattern': '.', + 'condition': 'host_os == "mac" and host_cpu == "x64"', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-browser-clang', + '-s', 'src/tools/clang/dsymutil/bin/dsymutil.x64.sha1', + '-o', 'src/tools/clang/dsymutil/bin/dsymutil', + ], + }, # Pull clang-format binaries using checked-in hashes. { 'name': 'clang_format_win', 'pattern': '.', 'condition': 'host_os == "win"', - 'action': [ 'download_from_google_storage', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--platform=win32', '--no_auth', @@ -2519,22 +2310,38 @@ hooks = [ ], }, { - 'name': 'clang_format_mac', + 'name': 'clang_format_mac_x64', 'pattern': '.', - 'condition': 'host_os == "mac"', - 'action': [ 'download_from_google_storage', + 'condition': 'host_os == "mac" and host_cpu == "x64"', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--platform=darwin', '--no_auth', '--bucket', 'chromium-clang-format', - '-s', 'src/buildtools/mac/clang-format.sha1', + '-s', 'src/buildtools/mac/clang-format.x64.sha1', + '-o', 'src/buildtools/mac/clang-format', ], }, + { + 'name': 'clang_format_mac_arm64', + 'pattern': '.', + 'condition': 'host_os == "mac" and host_cpu == "arm64"', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-clang-format', + '-s', 'src/buildtools/mac/clang-format.arm64.sha1', + '-o', 'src/buildtools/mac/clang-format', + ], + }, { 'name': 'clang_format_linux', 'pattern': '.', 'condition': 'host_os == "linux"', - 'action': [ 'download_from_google_storage', + 'action': [ 'python3', + 'src/third_party/depot_tools/download_from_google_storage.py', '--no_resume', '--platform=linux*', '--no_auth', diff --git a/OWNERS b/OWNERS index 4702befd9e..6ae4b59a95 100644 --- a/OWNERS +++ b/OWNERS @@ -18,3 +18,5 @@ per-file WATCHLISTS=* per-file native-api.md=mbonadei@webrtc.org per-file ....lua=titovartem@webrtc.org per-file .style.yapf=jleconte@webrtc.org +per-file *.py=jansson@webrtc.org +per-file *.py=jleconte@webrtc.org diff --git a/PRESUBMIT.py b/PRESUBMIT.py index b83f262e3d..cf7d261138 100755 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -26,7 +26,6 @@ 'examples/objc', 'media/base/stream_params.h', 'media/base/video_common.h', - 'media/sctp/usrsctp_transport.cc', 'modules/audio_coding', 'modules/audio_device', 'modules/audio_processing', @@ -819,21 +818,23 @@ def RunPythonTests(input_api, output_api): def Join(*args): return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) + excluded_files = [ + # These tests should be run manually after webrtc_dashboard_upload target + # has been built. + 'catapult_uploader_test.py', + 'process_perf_results_test.py', + ] + test_directories = [ input_api.PresubmitLocalPath(), Join('rtc_tools', 'py_event_log_analyzer'), Join('audio', 'test', 'unittests'), ] + [ root for root, _, files in os.walk(Join('tools_webrtc')) if any( - f.endswith('_test.py') for f in files) + f.endswith('_test.py') and f not in excluded_files for f in files) ] tests = [] - skipped_tests = [ - # This test should be run manually after webrtc_dashboard_upload target - # has been built. - r'catapult_uploader_test\.py$' - ] for directory in test_directories: tests.extend( @@ -842,7 +843,6 @@ def Join(*args): output_api, directory, files_to_check=[r'.+_test\.py$'], - files_to_skip=skipped_tests, run_on_python2=False)) return input_api.RunTests(tests, parallel=True) @@ -936,8 +936,6 @@ def CommonChecks(input_api, output_api): r'^testing[\\\/].*\.py$', r'^third_party[\\\/].*\.py$', r'^tools[\\\/].*\.py$', - # TODO(bugs.webrtc.org/13605): should arguably be checked. - r'^tools_webrtc[\\\/]mb[\\\/].*\.py$', r'^xcodebuild.*[\\\/].*\.py$', ), pylintrc='pylintrc', diff --git a/api/BUILD.gn b/api/BUILD.gn index 12e0c5cafa..569148065e 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -52,10 +52,11 @@ if (!build_with_chromium) { "../media:rtc_media_base", "../modules/audio_device:audio_device_api", "../modules/audio_processing:api", - "../pc:peerconnection", + "../pc:peer_connection_factory", + "../pc:webrtc_sdp", "../rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:threading", + "../stats:rtc_stats", "audio:audio_mixer_api", "audio_codecs:audio_codecs_api", "task_queue:default_task_queue_factory", @@ -91,7 +92,7 @@ rtc_library("rtp_packet_info") { ":refcountedbase", ":rtp_headers", ":scoped_refptr", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", "../rtc_base/system:rtc_export", "units:timestamp", ] @@ -116,10 +117,12 @@ rtc_library("media_stream_interface") { ":audio_options_api", ":rtp_parameters", ":scoped_refptr", + ":sequence_checker", ":video_track_source_constraints", "../modules/audio_processing:audio_processing_statistics", "../rtc_base:checks", "../rtc_base:refcount", + "../rtc_base/system:no_unique_address", "../rtc_base/system:rtc_export", "video:recordable_encoded_frame", "video:video_frame", @@ -169,6 +172,8 @@ rtc_library("libjingle_peerconnection_api") { ":audio_options_api", ":callfactory_api", ":fec_controller_api", + ":field_trials", + ":field_trials_view", ":frame_transformer_interface", ":libjingle_logging_api", ":media_stream_interface", @@ -183,7 +188,11 @@ rtc_library("libjingle_peerconnection_api") { ":scoped_refptr", ":sequence_checker", "../call:rtp_interfaces", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:logging", "../rtc_base:network_constants", + "../rtc_base:refcount", + "../rtc_base:stringutils", "adaptation:resource_adaptation_api", "audio:audio_mixer_api", "audio_codecs:audio_codecs_api", @@ -198,7 +207,6 @@ rtc_library("libjingle_peerconnection_api") { "transport:enums", "transport:network_control", "transport:sctp_transport_factory_interface", - "transport:webrtc_key_value_config", "transport/rtp:rtp_source", "units:data_rate", "units:timestamp", @@ -216,7 +224,6 @@ rtc_library("libjingle_peerconnection_api") { "../rtc_base", "../rtc_base:checks", "../rtc_base:ip_address", - "../rtc_base:rtc_base_approved", "../rtc_base:socket_address", "../rtc_base:threading", "../rtc_base/system:rtc_export", @@ -424,9 +431,13 @@ rtc_source_set("peer_network_dependencies") { rtc_source_set("peer_connection_quality_test_fixture_api") { visibility = [ "*" ] testonly = true - sources = [ "test/peerconnection_quality_test_fixture.h" ] + sources = [ + "test/peerconnection_quality_test_fixture.cc", + "test/peerconnection_quality_test_fixture.h", + ] deps = [ + ":array_view", ":audio_quality_analyzer_api", ":callfactory_api", ":fec_controller_api", @@ -443,8 +454,11 @@ rtc_source_set("peer_connection_quality_test_fixture_api") { ":track_id_stream_info_map", ":video_quality_analyzer_api", "../media:rtc_media_base", + "../modules/audio_processing:api", + "../rtc_base:checks", "../rtc_base:rtc_base", "../rtc_base:threading", + "audio:audio_mixer_api", "rtc_event_log", "task_queue", "transport:network_control", @@ -587,7 +601,7 @@ rtc_library("rtc_event_log_output_file") { deps = [ ":libjingle_logging_api", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", "../rtc_base/system:file_wrapper", "rtc_event_log", ] @@ -607,7 +621,7 @@ rtc_source_set("rtc_stats_api") { ":scoped_refptr", "../api:refcountedbase", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", "../rtc_base/system:rtc_export", ] } @@ -652,7 +666,11 @@ rtc_source_set("bitrate_allocation") { rtc_source_set("simulated_network_api") { visibility = [ "*" ] sources = [ "test/simulated_network.h" ] - deps = [ "../rtc_base" ] + deps = [ + "../rtc_base", + "../rtc_base:macromagic", + "../rtc_base:random", + ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -832,7 +850,6 @@ if (rtc_include_tests) { deps = [ ":simulcast_test_fixture_api", "../modules/video_coding:simulcast_test_fixture_impl", - "../rtc_base:rtc_base_approved", "video_codecs:video_codecs_api", ] } @@ -864,7 +881,6 @@ if (rtc_include_tests) { ":videocodec_test_fixture_api", "../modules/video_coding:video_codecs_test_framework", "../modules/video_coding:videocodec_test_impl", - "../rtc_base:rtc_base_approved", "video_codecs:video_codecs_api", ] } @@ -903,6 +919,17 @@ if (rtc_include_tests) { ] } + rtc_source_set("mock_dtmf_sender") { + visibility = [ "*" ] + testonly = true + sources = [ "test/mock_dtmf_sender.h" ] + + deps = [ + ":libjingle_peerconnection_api", + "../test:test_support", + ] + } + rtc_source_set("mock_fec_controller_override") { visibility = [ "*" ] testonly = true @@ -948,7 +975,7 @@ if (rtc_include_tests) { ":libjingle_peerconnection_api", ":rtp_parameters", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", "crypto:frame_encryptor_interface", ] } @@ -965,7 +992,7 @@ if (rtc_include_tests) { ":libjingle_peerconnection_api", ":rtp_parameters", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", "crypto:frame_decryptor_interface", ] } @@ -1148,6 +1175,7 @@ if (rtc_include_tests) { sources = [ "array_view_unittest.cc", + "field_trials_unittest.cc", "function_view_unittest.cc", "rtc_error_unittest.cc", "rtc_event_log_output_file_unittest.cc", @@ -1157,13 +1185,17 @@ if (rtc_include_tests) { "scoped_refptr_unittest.cc", "sequence_checker_unittest.cc", "test/create_time_controller_unittest.cc", + "test/peerconnection_quality_test_fixture_unittest.cc", ] deps = [ ":array_view", ":create_time_controller", + ":field_trials", + ":field_trials_view", ":function_view", ":libjingle_peerconnection_api", + ":peer_connection_quality_test_fixture_api", ":rtc_error", ":rtc_event_log_output_file", ":rtp_packet_info", @@ -1171,21 +1203,28 @@ if (rtc_include_tests) { ":scoped_refptr", ":sequence_checker", ":time_controller", + "../rtc_base:buffer", "../rtc_base:checks", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:platform_thread", + "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", "../rtc_base:task_queue_for_test", "../rtc_base/task_utils:repeating_task", + "../system_wrappers:field_trial", "../test:fileutils", + "../test:rtc_expect_death", "../test:test_support", "task_queue:task_queue_default_factory_unittests", + "transport:field_trial_based_config", "units:time_delta", "units:timestamp", "units:units_unittests", + "video:frame_buffer_unittest", "video:rtp_video_frame_assembler_unittests", "video:video_unittests", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("compile_all_headers") { @@ -1201,6 +1240,7 @@ if (rtc_include_tests) { ":mock_audio_mixer", ":mock_audio_sink", ":mock_data_channel", + ":mock_dtmf_sender", ":mock_frame_decryptor", ":mock_frame_encryptor", ":mock_media_stream_interface", @@ -1219,3 +1259,31 @@ if (rtc_include_tests) { ] } } + +rtc_source_set("field_trials_view") { + visibility = [ "*" ] + sources = [ "field_trials_view.h" ] + deps = [ "../rtc_base/system:rtc_export" ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_source_set("webrtc_key_value_config") { + visibility = [ "*" ] + sources = [ "webrtc_key_value_config.h" ] + deps = [ ":field_trials_view" ] +} + +rtc_library("field_trials") { + visibility = [ "*" ] + sources = [ + "field_trials.cc", + "field_trials.h", + ] + deps = [ + ":field_trials_view", + "../rtc_base:checks", + "../rtc_base/containers:flat_map", + "../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} diff --git a/api/DEPS b/api/DEPS index ff493bf77f..9db91bef37 100644 --- a/api/DEPS +++ b/api/DEPS @@ -274,6 +274,10 @@ specific_include_rules = { "+rtc_base/ref_counted_object.h", ], + "notifier\.h": [ + "+rtc_base/system/no_unique_address.h", + ], + "simulated_network\.h": [ "+rtc_base/random.h", "+rtc_base/thread_annotations.h", @@ -308,6 +312,14 @@ specific_include_rules = { "+rtc_base/thread_annotations.h", ], + "video_encoder_factory_template.*\.h": [ + "+modules/video_coding", + ], + + "field_trials\.h": [ + "+rtc_base/containers/flat_map.h", + ], + # .cc files in api/ should not be restricted in what they can #include, # so we re-add all the top-level directories here. (That's because .h # files leak their #includes to whoever's #including them, but .cc files diff --git a/api/adaptation/BUILD.gn b/api/adaptation/BUILD.gn index 2cba5f407e..839ad2c24b 100644 --- a/api/adaptation/BUILD.gn +++ b/api/adaptation/BUILD.gn @@ -18,7 +18,6 @@ rtc_source_set("resource_adaptation_api") { "../../api:scoped_refptr", "../../rtc_base:checks", "../../rtc_base:refcount", - "../../rtc_base:rtc_base_approved", "../../rtc_base/system:rtc_export", ] } diff --git a/api/adaptation/DEPS b/api/adaptation/DEPS index cab7fb8e14..734e152497 100644 --- a/api/adaptation/DEPS +++ b/api/adaptation/DEPS @@ -1,6 +1,6 @@ specific_include_rules = { "resource\.h": [ - # ref_count.h is a public_deps of rtc_base_approved. Necessary because of + # ref_count.h is a public_deps of rtc_base:refcount. Necessary because of # rtc::RefCountInterface. "+rtc_base/ref_count.h", ], diff --git a/api/audio/BUILD.gn b/api/audio/BUILD.gn index 49cf95dbce..7220106d3c 100644 --- a/api/audio/BUILD.gn +++ b/api/audio/BUILD.gn @@ -20,7 +20,9 @@ rtc_library("audio_frame_api") { deps = [ "..:rtp_packet_info", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:timeutils", ] } @@ -35,7 +37,7 @@ rtc_source_set("audio_mixer_api") { deps = [ ":audio_frame_api", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", ] } @@ -47,7 +49,6 @@ rtc_library("aec3_config") { ] deps = [ "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base:safe_minmax", "../../rtc_base/system:rtc_export", ] @@ -63,8 +64,9 @@ rtc_library("aec3_config_json") { deps = [ ":aec3_config", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", "../../rtc_base:rtc_json", + "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] @@ -82,7 +84,6 @@ rtc_library("aec3_factory") { ":aec3_config", ":echo_control", "../../modules/audio_processing/aec3", - "../../rtc_base:rtc_base_approved", "../../rtc_base/system:rtc_export", ] } diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h index 1fd403652a..bc9718c3e2 100644 --- a/api/audio/echo_canceller3_config.h +++ b/api/audio/echo_canceller3_config.h @@ -236,6 +236,13 @@ struct RTC_EXPORT EchoCanceller3Config { float floor_first_increase = 0.00001f; bool conservative_hf_suppression = false; } suppressor; + + struct MultiChannel { + bool detect_stereo_content = true; + float stereo_detection_threshold = 0.0f; + int stereo_detection_timeout_threshold_seconds = 300; + float stereo_detection_hysteresis_seconds = 2.0f; + } multi_channel; }; } // namespace webrtc diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc index 71966c13b3..e41612eb14 100644 --- a/api/audio/echo_canceller3_config_json.cc +++ b/api/audio/echo_canceller3_config_json.cc @@ -415,6 +415,17 @@ void Aec3ConfigFromJsonString(absl::string_view json_string, ReadParam(section, "conservative_hf_suppression", &cfg.suppressor.conservative_hf_suppression); } + + if (rtc::GetValueFromJsonObject(aec3_root, "multi_channel", §ion)) { + ReadParam(section, "detect_stereo_content", + &cfg.multi_channel.detect_stereo_content); + ReadParam(section, "stereo_detection_threshold", + &cfg.multi_channel.stereo_detection_threshold); + ReadParam(section, "stereo_detection_timeout_threshold_seconds", + &cfg.multi_channel.stereo_detection_timeout_threshold_seconds); + ReadParam(section, "stereo_detection_hysteresis_seconds", + &cfg.multi_channel.stereo_detection_hysteresis_seconds); + } } EchoCanceller3Config Aec3ConfigFromJsonString(absl::string_view json_string) { @@ -574,7 +585,8 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) { ost << "\"erle_onset_compensation_in_dominant_nearend\": " << (config.ep_strength.erle_onset_compensation_in_dominant_nearend ? "true" - : "false") << ","; + : "false") + << ","; ost << "\"use_conservative_tail_frequency_response\": " << (config.ep_strength.use_conservative_tail_frequency_response ? "true" @@ -736,7 +748,19 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) { << ","; ost << "\"conservative_hf_suppression\": " << config.suppressor.conservative_hf_suppression; + ost << "},"; + + ost << "\"multi_channel\": {"; + ost << "\"detect_stereo_content\": " + << (config.multi_channel.detect_stereo_content ? "true" : "false") << ","; + ost << "\"stereo_detection_threshold\": " + << config.multi_channel.stereo_detection_threshold << ","; + ost << "\"stereo_detection_timeout_threshold_seconds\": " + << config.multi_channel.stereo_detection_timeout_threshold_seconds << ","; + ost << "\"stereo_detection_hysteresis_seconds\": " + << config.multi_channel.stereo_detection_hysteresis_seconds; ost << "}"; + ost << "}"; ost << "}"; diff --git a/api/audio/echo_canceller3_factory.cc b/api/audio/echo_canceller3_factory.cc index d65a7262fa..284b117bea 100644 --- a/api/audio/echo_canceller3_factory.cc +++ b/api/audio/echo_canceller3_factory.cc @@ -25,7 +25,8 @@ std::unique_ptr EchoCanceller3Factory::Create( int num_render_channels, int num_capture_channels) { return std::make_unique( - config_, sample_rate_hz, num_render_channels, num_capture_channels); + config_, /*multichannel_config=*/absl::nullopt, sample_rate_hz, + num_render_channels, num_capture_channels); } } // namespace webrtc diff --git a/api/audio/test/BUILD.gn b/api/audio/test/BUILD.gn index d62baf15b7..dfe8c32f80 100644 --- a/api/audio/test/BUILD.gn +++ b/api/audio/test/BUILD.gn @@ -24,7 +24,6 @@ if (rtc_include_tests) { "..:aec3_config", "..:aec3_config_json", "..:audio_frame_api", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", ] } diff --git a/api/audio/test/echo_canceller3_config_json_unittest.cc b/api/audio/test/echo_canceller3_config_json_unittest.cc index bb28b4feb3..4146dda9fe 100644 --- a/api/audio/test/echo_canceller3_config_json_unittest.cc +++ b/api/audio/test/echo_canceller3_config_json_unittest.cc @@ -31,6 +31,11 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) { 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; + cfg.multi_channel.detect_stereo_content = + !cfg.multi_channel.detect_stereo_content; + cfg.multi_channel.stereo_detection_threshold += 1.0f; + cfg.multi_channel.stereo_detection_timeout_threshold_seconds += 1; + cfg.multi_channel.stereo_detection_hysteresis_seconds += 1; std::string json_string = Aec3ConfigToJsonString(cfg); EchoCanceller3Config cfg_transformed = Aec3ConfigFromJsonString(json_string); @@ -75,5 +80,14 @@ TEST(EchoCanceller3JsonHelpers, ToStringAndParseJson) { 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); + EXPECT_EQ(cfg.multi_channel.detect_stereo_content, + cfg_transformed.multi_channel.detect_stereo_content); + EXPECT_EQ(cfg.multi_channel.stereo_detection_threshold, + cfg_transformed.multi_channel.stereo_detection_threshold); + EXPECT_EQ( + cfg.multi_channel.stereo_detection_timeout_threshold_seconds, + cfg_transformed.multi_channel.stereo_detection_timeout_threshold_seconds); + EXPECT_EQ(cfg.multi_channel.stereo_detection_hysteresis_seconds, + cfg_transformed.multi_channel.stereo_detection_hysteresis_seconds); } } // namespace webrtc diff --git a/api/audio_codecs/BUILD.gn b/api/audio_codecs/BUILD.gn index 5926f5ec2e..ec358e12e2 100644 --- a/api/audio_codecs/BUILD.gn +++ b/api/audio_codecs/BUILD.gn @@ -32,8 +32,11 @@ rtc_library("audio_codecs_api") { "..:array_view", "..:bitrate_allocation", "..:scoped_refptr", + "../../api:field_trials_view", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:refcount", "../../rtc_base:sanitizer", "../../rtc_base/system:rtc_export", "../units:time_delta", @@ -54,7 +57,6 @@ rtc_library("builtin_audio_decoder_factory") { deps = [ ":audio_codecs_api", "..:scoped_refptr", - "../../rtc_base:rtc_base_approved", "L16:audio_decoder_L16", "g711:audio_decoder_g711", "g722:audio_decoder_g722", @@ -88,7 +90,6 @@ rtc_library("builtin_audio_encoder_factory") { deps = [ ":audio_codecs_api", "..:scoped_refptr", - "../../rtc_base:rtc_base_approved", "L16:audio_encoder_L16", "g711:audio_encoder_g711", "g722:audio_encoder_g722", @@ -122,7 +123,6 @@ rtc_library("opus_audio_decoder_factory") { deps = [ ":audio_codecs_api", "..:scoped_refptr", - "../../rtc_base:rtc_base_approved", "opus:audio_decoder_multiopus", "opus:audio_decoder_opus", ] @@ -138,7 +138,6 @@ rtc_library("opus_audio_encoder_factory") { deps = [ ":audio_codecs_api", "..:scoped_refptr", - "../../rtc_base:rtc_base_approved", "opus:audio_encoder_multiopus", "opus:audio_encoder_opus", ] diff --git a/api/audio_codecs/L16/BUILD.gn b/api/audio_codecs/L16/BUILD.gn index 1f7a1e5a0b..41e9eb42d8 100644 --- a/api/audio_codecs/L16/BUILD.gn +++ b/api/audio_codecs/L16/BUILD.gn @@ -21,9 +21,11 @@ rtc_library("audio_encoder_L16") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:pcm16b", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -41,8 +43,9 @@ rtc_library("audio_decoder_L16") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:pcm16b", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base/system:rtc_export", ] absl_deps = [ diff --git a/api/audio_codecs/L16/audio_decoder_L16.cc b/api/audio_codecs/L16/audio_decoder_L16.cc index 93863f1020..a03abe26f7 100644 --- a/api/audio_codecs/L16/audio_decoder_L16.cc +++ b/api/audio_codecs/L16/audio_decoder_L16.cc @@ -37,7 +37,8 @@ void AudioDecoderL16::AppendSupportedDecoders( std::unique_ptr AudioDecoderL16::MakeAudioDecoder( const Config& config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { return nullptr; } diff --git a/api/audio_codecs/L16/audio_decoder_L16.h b/api/audio_codecs/L16/audio_decoder_L16.h index 581a5b82c1..5a01b7dc01 100644 --- a/api/audio_codecs/L16/audio_decoder_L16.h +++ b/api/audio_codecs/L16/audio_decoder_L16.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -39,7 +40,8 @@ struct RTC_EXPORT AudioDecoderL16 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( const Config& config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/L16/audio_encoder_L16.cc b/api/audio_codecs/L16/audio_encoder_L16.cc index 590d3e32d9..20259b9ad8 100644 --- a/api/audio_codecs/L16/audio_encoder_L16.cc +++ b/api/audio_codecs/L16/audio_encoder_L16.cc @@ -59,7 +59,8 @@ AudioCodecInfo AudioEncoderL16::QueryAudioEncoder( std::unique_ptr AudioEncoderL16::MakeAudioEncoder( const AudioEncoderL16::Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { AudioEncoderPcm16B::Config c; c.sample_rate_hz = config.sample_rate_hz; c.num_channels = config.num_channels; diff --git a/api/audio_codecs/L16/audio_encoder_L16.h b/api/audio_codecs/L16/audio_encoder_L16.h index 25d221148e..47509849de 100644 --- a/api/audio_codecs/L16/audio_encoder_L16.h +++ b/api/audio_codecs/L16/audio_encoder_L16.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -44,7 +45,8 @@ struct RTC_EXPORT AudioEncoderL16 { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/audio_decoder_factory_template.h b/api/audio_codecs/audio_decoder_factory_template.h index 976f9c62d7..4e39365182 100644 --- a/api/audio_codecs/audio_decoder_factory_template.h +++ b/api/audio_codecs/audio_decoder_factory_template.h @@ -15,6 +15,7 @@ #include #include "api/audio_codecs/audio_decoder_factory.h" +#include "api/field_trials_view.h" #include "api/scoped_refptr.h" #include "rtc_base/ref_counted_object.h" @@ -32,7 +33,8 @@ struct Helper<> { static bool IsSupportedDecoder(const SdpAudioFormat& format) { return false; } static std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const FieldTrialsView* field_trials) { return nullptr; } }; @@ -55,16 +57,22 @@ struct Helper { } static std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const FieldTrialsView* field_trials) { auto opt_config = T::SdpToConfig(format); return opt_config ? T::MakeAudioDecoder(*opt_config, codec_pair_id) - : Helper::MakeAudioDecoder(format, codec_pair_id); + : Helper::MakeAudioDecoder(format, codec_pair_id, + field_trials); } }; template class AudioDecoderFactoryT : public AudioDecoderFactory { public: + explicit AudioDecoderFactoryT(const FieldTrialsView* field_trials) { + field_trials_ = field_trials; + } + std::vector GetSupportedDecoders() override { std::vector specs; Helper::AppendSupportedDecoders(&specs); @@ -78,8 +86,11 @@ class AudioDecoderFactoryT : public AudioDecoderFactory { std::unique_ptr MakeAudioDecoder( const SdpAudioFormat& format, absl::optional codec_pair_id) override { - return Helper::MakeAudioDecoder(format, codec_pair_id); + return Helper::MakeAudioDecoder(format, codec_pair_id, + field_trials_); } + + const FieldTrialsView* field_trials_; }; } // namespace audio_decoder_factory_template_impl @@ -115,7 +126,8 @@ class AudioDecoderFactoryT : public AudioDecoderFactory { // TODO(kwiberg): Point at CreateBuiltinAudioDecoderFactory() for an example of // how it is used. template -rtc::scoped_refptr CreateAudioDecoderFactory() { +rtc::scoped_refptr CreateAudioDecoderFactory( + const FieldTrialsView* field_trials = nullptr) { // There's no technical reason we couldn't allow zero template parameters, // but such a factory couldn't create any decoders, and callers can do this // by mistake by simply forgetting the <> altogether. So we forbid it in @@ -124,7 +136,8 @@ rtc::scoped_refptr CreateAudioDecoderFactory() { "Caller must give at least one template parameter"); return rtc::make_ref_counted< - audio_decoder_factory_template_impl::AudioDecoderFactoryT>(); + audio_decoder_factory_template_impl::AudioDecoderFactoryT>( + field_trials); } } // namespace webrtc diff --git a/api/audio_codecs/audio_encoder_factory_template.h b/api/audio_codecs/audio_encoder_factory_template.h index 4dc0672c46..8490f46b7d 100644 --- a/api/audio_codecs/audio_encoder_factory_template.h +++ b/api/audio_codecs/audio_encoder_factory_template.h @@ -15,6 +15,7 @@ #include #include "api/audio_codecs/audio_encoder_factory.h" +#include "api/field_trials_view.h" #include "api/scoped_refptr.h" #include "rtc_base/ref_counted_object.h" @@ -36,7 +37,8 @@ struct Helper<> { static std::unique_ptr MakeAudioEncoder( int payload_type, const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const FieldTrialsView* field_trials) { return nullptr; } }; @@ -63,13 +65,14 @@ struct Helper { static std::unique_ptr MakeAudioEncoder( int payload_type, const SdpAudioFormat& format, - absl::optional codec_pair_id) { + absl::optional codec_pair_id, + const FieldTrialsView* field_trials) { auto opt_config = T::SdpToConfig(format); if (opt_config) { return T::MakeAudioEncoder(*opt_config, payload_type, codec_pair_id); } else { return Helper::MakeAudioEncoder(payload_type, format, - codec_pair_id); + codec_pair_id, field_trials); } } }; @@ -77,6 +80,10 @@ struct Helper { template class AudioEncoderFactoryT : public AudioEncoderFactory { public: + explicit AudioEncoderFactoryT(const FieldTrialsView* field_trials) { + field_trials_ = field_trials; + } + std::vector GetSupportedEncoders() override { std::vector specs; Helper::AppendSupportedEncoders(&specs); @@ -92,8 +99,11 @@ class AudioEncoderFactoryT : public AudioEncoderFactory { int payload_type, const SdpAudioFormat& format, absl::optional codec_pair_id) override { - return Helper::MakeAudioEncoder(payload_type, format, codec_pair_id); + return Helper::MakeAudioEncoder(payload_type, format, codec_pair_id, + field_trials_); } + + const FieldTrialsView* field_trials_; }; } // namespace audio_encoder_factory_template_impl @@ -134,7 +144,8 @@ class AudioEncoderFactoryT : public AudioEncoderFactory { // TODO(kwiberg): Point at CreateBuiltinAudioEncoderFactory() for an example of // how it is used. template -rtc::scoped_refptr CreateAudioEncoderFactory() { +rtc::scoped_refptr CreateAudioEncoderFactory( + const FieldTrialsView* field_trials = nullptr) { // There's no technical reason we couldn't allow zero template parameters, // but such a factory couldn't create any encoders, and callers can do this // by mistake by simply forgetting the <> altogether. So we forbid it in @@ -143,7 +154,8 @@ rtc::scoped_refptr CreateAudioEncoderFactory() { "Caller must give at least one template parameter"); return rtc::make_ref_counted< - audio_encoder_factory_template_impl::AudioEncoderFactoryT>(); + audio_encoder_factory_template_impl::AudioEncoderFactoryT>( + field_trials); } } // namespace webrtc diff --git a/api/audio_codecs/builtin_audio_encoder_factory.cc b/api/audio_codecs/builtin_audio_encoder_factory.cc index 99fac09a57..530d64b2ba 100644 --- a/api/audio_codecs/builtin_audio_encoder_factory.cc +++ b/api/audio_codecs/builtin_audio_encoder_factory.cc @@ -47,8 +47,10 @@ struct NotAdvertised { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt) { - return T::MakeAudioEncoder(config, payload_type, codec_pair_id); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr) { + return T::MakeAudioEncoder(config, payload_type, codec_pair_id, + field_trials); } }; diff --git a/api/audio_codecs/g711/BUILD.gn b/api/audio_codecs/g711/BUILD.gn index 92d77bed9f..b2ff324f12 100644 --- a/api/audio_codecs/g711/BUILD.gn +++ b/api/audio_codecs/g711/BUILD.gn @@ -21,9 +21,11 @@ rtc_library("audio_encoder_g711") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:g711", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -41,8 +43,9 @@ rtc_library("audio_decoder_g711") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:g711", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base/system:rtc_export", ] absl_deps = [ diff --git a/api/audio_codecs/g711/audio_decoder_g711.cc b/api/audio_codecs/g711/audio_decoder_g711.cc index f3d3378cf2..838f7e9624 100644 --- a/api/audio_codecs/g711/audio_decoder_g711.cc +++ b/api/audio_codecs/g711/audio_decoder_g711.cc @@ -47,7 +47,8 @@ void AudioDecoderG711::AppendSupportedDecoders( std::unique_ptr AudioDecoderG711::MakeAudioDecoder( const Config& config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/g711/audio_decoder_g711.h b/api/audio_codecs/g711/audio_decoder_g711.h index 18c15a8d60..0f7a98d345 100644 --- a/api/audio_codecs/g711/audio_decoder_g711.h +++ b/api/audio_codecs/g711/audio_decoder_g711.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -39,7 +40,8 @@ struct RTC_EXPORT AudioDecoderG711 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( const Config& config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g711/audio_encoder_g711.cc b/api/audio_codecs/g711/audio_encoder_g711.cc index 4c1ce0f8e6..1dca3b80d3 100644 --- a/api/audio_codecs/g711/audio_encoder_g711.cc +++ b/api/audio_codecs/g711/audio_encoder_g711.cc @@ -64,7 +64,8 @@ AudioCodecInfo AudioEncoderG711::QueryAudioEncoder(const Config& config) { std::unique_ptr AudioEncoderG711::MakeAudioEncoder( const Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/g711/audio_encoder_g711.h b/api/audio_codecs/g711/audio_encoder_g711.h index 29fe38f1a0..4b3eb845e0 100644 --- a/api/audio_codecs/g711/audio_encoder_g711.h +++ b/api/audio_codecs/g711/audio_encoder_g711.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -44,7 +45,8 @@ struct RTC_EXPORT AudioEncoderG711 { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g722/BUILD.gn b/api/audio_codecs/g722/BUILD.gn index a186eabbb7..d78ee75984 100644 --- a/api/audio_codecs/g722/BUILD.gn +++ b/api/audio_codecs/g722/BUILD.gn @@ -27,9 +27,11 @@ rtc_library("audio_encoder_g722") { deps = [ ":audio_encoder_g722_config", "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:g722", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -47,8 +49,9 @@ rtc_library("audio_decoder_g722") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:g722", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base/system:rtc_export", ] absl_deps = [ diff --git a/api/audio_codecs/g722/audio_decoder_g722.cc b/api/audio_codecs/g722/audio_decoder_g722.cc index 0049e5ab32..ed7163471a 100644 --- a/api/audio_codecs/g722/audio_decoder_g722.cc +++ b/api/audio_codecs/g722/audio_decoder_g722.cc @@ -36,7 +36,8 @@ void AudioDecoderG722::AppendSupportedDecoders( std::unique_ptr AudioDecoderG722::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/g722/audio_decoder_g722.h b/api/audio_codecs/g722/audio_decoder_g722.h index 2a674926db..6f7b253039 100644 --- a/api/audio_codecs/g722/audio_decoder_g722.h +++ b/api/audio_codecs/g722/audio_decoder_g722.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -33,7 +34,8 @@ struct RTC_EXPORT AudioDecoderG722 { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/g722/audio_encoder_g722.cc b/api/audio_codecs/g722/audio_encoder_g722.cc index 66cf9e19d6..56a6c4da6a 100644 --- a/api/audio_codecs/g722/audio_encoder_g722.cc +++ b/api/audio_codecs/g722/audio_encoder_g722.cc @@ -62,7 +62,8 @@ AudioCodecInfo AudioEncoderG722::QueryAudioEncoder( std::unique_ptr AudioEncoderG722::MakeAudioEncoder( const AudioEncoderG722Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/g722/audio_encoder_g722.h b/api/audio_codecs/g722/audio_encoder_g722.h index 327c0af04a..78ceddd1e9 100644 --- a/api/audio_codecs/g722/audio_encoder_g722.h +++ b/api/audio_codecs/g722/audio_encoder_g722.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/g722/audio_encoder_g722_config.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioEncoderG722 { static std::unique_ptr MakeAudioEncoder( const AudioEncoderG722Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/ilbc/BUILD.gn b/api/audio_codecs/ilbc/BUILD.gn index b6a5045eaf..22cf48220f 100644 --- a/api/audio_codecs/ilbc/BUILD.gn +++ b/api/audio_codecs/ilbc/BUILD.gn @@ -27,9 +27,11 @@ rtc_library("audio_encoder_ilbc") { deps = [ ":audio_encoder_ilbc_config", "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:ilbc", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings", @@ -46,8 +48,8 @@ rtc_library("audio_decoder_ilbc") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:ilbc", - "../../../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings", diff --git a/api/audio_codecs/ilbc/audio_decoder_ilbc.cc b/api/audio_codecs/ilbc/audio_decoder_ilbc.cc index 237cef23c1..c58316903a 100644 --- a/api/audio_codecs/ilbc/audio_decoder_ilbc.cc +++ b/api/audio_codecs/ilbc/audio_decoder_ilbc.cc @@ -34,7 +34,8 @@ void AudioDecoderIlbc::AppendSupportedDecoders( std::unique_ptr AudioDecoderIlbc::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { return std::make_unique(); } diff --git a/api/audio_codecs/ilbc/audio_decoder_ilbc.h b/api/audio_codecs/ilbc/audio_decoder_ilbc.h index 9ab847977d..60566c88df 100644 --- a/api/audio_codecs/ilbc/audio_decoder_ilbc.h +++ b/api/audio_codecs/ilbc/audio_decoder_ilbc.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" namespace webrtc { @@ -29,7 +30,8 @@ struct AudioDecoderIlbc { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/ilbc/audio_encoder_ilbc.cc b/api/audio_codecs/ilbc/audio_encoder_ilbc.cc index 52ba8f6b88..b497948491 100644 --- a/api/audio_codecs/ilbc/audio_encoder_ilbc.cc +++ b/api/audio_codecs/ilbc/audio_encoder_ilbc.cc @@ -76,7 +76,8 @@ AudioCodecInfo AudioEncoderIlbc::QueryAudioEncoder( std::unique_ptr AudioEncoderIlbc::MakeAudioEncoder( const AudioEncoderIlbcConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/ilbc/audio_encoder_ilbc.h b/api/audio_codecs/ilbc/audio_encoder_ilbc.h index e4aeca70de..a5306841ce 100644 --- a/api/audio_codecs/ilbc/audio_encoder_ilbc.h +++ b/api/audio_codecs/ilbc/audio_encoder_ilbc.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/ilbc/audio_encoder_ilbc_config.h" +#include "api/field_trials_view.h" namespace webrtc { @@ -33,7 +34,8 @@ struct AudioEncoderIlbc { static std::unique_ptr MakeAudioEncoder( const AudioEncoderIlbcConfig& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/BUILD.gn b/api/audio_codecs/isac/BUILD.gn index 6ff6e5f092..96a0ed5013 100644 --- a/api/audio_codecs/isac/BUILD.gn +++ b/api/audio_codecs/isac/BUILD.gn @@ -65,8 +65,9 @@ rtc_library("audio_encoder_isac_fix") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:isac_fix", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:stringutils", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -84,8 +85,8 @@ rtc_library("audio_decoder_isac_fix") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:isac_fix", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -103,8 +104,9 @@ rtc_library("audio_encoder_isac_float") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:isac", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:stringutils", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -122,8 +124,8 @@ rtc_library("audio_decoder_isac_float") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:isac", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.cc b/api/audio_codecs/isac/audio_decoder_isac_fix.cc index 305e15a525..b3ab91da47 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.cc +++ b/api/audio_codecs/isac/audio_decoder_isac_fix.cc @@ -33,7 +33,8 @@ void AudioDecoderIsacFix::AppendSupportedDecoders( std::unique_ptr AudioDecoderIsacFix::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { AudioDecoderIsacFixImpl::Config c; c.sample_rate_hz = 16000; return std::make_unique(c); diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.h b/api/audio_codecs/isac/audio_decoder_isac_fix.h index 200914adfe..8f61d9ab0e 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.h +++ b/api/audio_codecs/isac/audio_decoder_isac_fix.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -30,7 +31,8 @@ struct RTC_EXPORT AudioDecoderIsacFix { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.cc b/api/audio_codecs/isac/audio_decoder_isac_float.cc index 683eb6c0ad..98f672b468 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_float.cc +++ b/api/audio_codecs/isac/audio_decoder_isac_float.cc @@ -42,7 +42,8 @@ void AudioDecoderIsacFloat::AppendSupportedDecoders( std::unique_ptr AudioDecoderIsacFloat::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { AudioDecoderIsacFloatImpl::Config c; c.sample_rate_hz = config.sample_rate_hz; if (!config.IsOk()) { diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.h b/api/audio_codecs/isac/audio_decoder_isac_float.h index e78f8b81ee..864c6b999f 100644 --- a/api/audio_codecs/isac/audio_decoder_isac_float.h +++ b/api/audio_codecs/isac/audio_decoder_isac_float.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -35,7 +36,8 @@ struct RTC_EXPORT AudioDecoderIsacFloat { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.cc b/api/audio_codecs/isac/audio_encoder_isac_fix.cc index b590be1ea3..39603775a4 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.cc +++ b/api/audio_codecs/isac/audio_encoder_isac_fix.cc @@ -56,7 +56,8 @@ AudioCodecInfo AudioEncoderIsacFix::QueryAudioEncoder( std::unique_ptr AudioEncoderIsacFix::MakeAudioEncoder( AudioEncoderIsacFix::Config config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { AudioEncoderIsacFixImpl::Config c; c.frame_size_ms = config.frame_size_ms; c.bit_rate = config.bit_rate; diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.h b/api/audio_codecs/isac/audio_encoder_isac_fix.h index e50d9f5112..de0f1d1308 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.h +++ b/api/audio_codecs/isac/audio_encoder_isac_fix.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -44,7 +45,8 @@ struct RTC_EXPORT AudioEncoderIsacFix { static std::unique_ptr MakeAudioEncoder( Config config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.cc b/api/audio_codecs/isac/audio_encoder_isac_float.cc index e2afeae84e..e3e50080fa 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_float.cc +++ b/api/audio_codecs/isac/audio_encoder_isac_float.cc @@ -68,7 +68,8 @@ AudioCodecInfo AudioEncoderIsacFloat::QueryAudioEncoder( std::unique_ptr AudioEncoderIsacFloat::MakeAudioEncoder( const AudioEncoderIsacFloat::Config& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { AudioEncoderIsacFloatImpl::Config c; c.payload_type = payload_type; c.sample_rate_hz = config.sample_rate_hz; diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.h b/api/audio_codecs/isac/audio_encoder_isac_float.h index 0cb9c17d71..d031d76db1 100644 --- a/api/audio_codecs/isac/audio_encoder_isac_float.h +++ b/api/audio_codecs/isac/audio_encoder_isac_float.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -58,7 +59,8 @@ struct RTC_EXPORT AudioEncoderIsacFloat { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/BUILD.gn b/api/audio_codecs/opus/BUILD.gn index 586e9b3dd8..2091cbd000 100644 --- a/api/audio_codecs/opus/BUILD.gn +++ b/api/audio_codecs/opus/BUILD.gn @@ -21,7 +21,6 @@ rtc_library("audio_encoder_opus_config") { "audio_encoder_opus_config.h", ] deps = [ - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -46,8 +45,8 @@ rtc_library("audio_encoder_opus") { deps = [ ":audio_encoder_opus_config", "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:webrtc_opus", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -65,8 +64,8 @@ rtc_library("audio_decoder_opus") { ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:webrtc_opus", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ @@ -82,8 +81,8 @@ rtc_library("audio_encoder_multiopus") { sources = [ "audio_encoder_multi_channel_opus.cc" ] deps = [ "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:webrtc_multiopus", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", "../opus:audio_encoder_opus_config", ] @@ -100,8 +99,8 @@ rtc_library("audio_decoder_multiopus") { deps = [ ":audio_decoder_opus_config", "..:audio_codecs_api", + "../../../api:field_trials_view", "../../../modules/audio_coding:webrtc_multiopus", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:rtc_export", ] absl_deps = [ diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc index 6ba2b6d9d3..0fb4e05511 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.cc @@ -64,7 +64,8 @@ void AudioDecoderMultiChannelOpus::AppendSupportedDecoders( std::unique_ptr AudioDecoderMultiChannelOpus::MakeAudioDecoder( AudioDecoderMultiChannelOpusConfig config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { return AudioDecoderMultiChannelOpusImpl::MakeAudioDecoder(config); } } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h index b5ca0fe41b..eafd6c6939 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -32,7 +33,8 @@ struct RTC_EXPORT AudioDecoderMultiChannelOpus { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( AudioDecoderMultiChannelOpusConfig config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_decoder_opus.cc b/api/audio_codecs/opus/audio_decoder_opus.cc index 7e0d88b7ad..efc9a73546 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.cc +++ b/api/audio_codecs/opus/audio_decoder_opus.cc @@ -73,7 +73,8 @@ void AudioDecoderOpus::AppendSupportedDecoders( std::unique_ptr AudioDecoderOpus::MakeAudioDecoder( Config config, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/opus/audio_decoder_opus.h b/api/audio_codecs/opus/audio_decoder_opus.h index ec0f61d5bb..138c0377df 100644 --- a/api/audio_codecs/opus/audio_decoder_opus.h +++ b/api/audio_codecs/opus/audio_decoder_opus.h @@ -18,6 +18,7 @@ #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/audio_codecs/audio_decoder.h" #include "api/audio_codecs/audio_format.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioDecoderOpus { static void AppendSupportedDecoders(std::vector* specs); static std::unique_ptr MakeAudioDecoder( Config config, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc index 758eaaeebe..14f480b1ec 100644 --- a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc +++ b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.cc @@ -66,7 +66,8 @@ AudioCodecInfo AudioEncoderMultiChannelOpus::QueryAudioEncoder( std::unique_ptr AudioEncoderMultiChannelOpus::MakeAudioEncoder( const AudioEncoderMultiChannelOpusConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { return AudioEncoderMultiChannelOpusImpl::MakeAudioEncoder(config, payload_type); } diff --git a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h index 977a3a4b9c..c1c4db3577 100644 --- a/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h +++ b/api/audio_codecs/opus/audio_encoder_multi_channel_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus_config.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -33,7 +34,8 @@ struct RTC_EXPORT AudioEncoderMultiChannelOpus { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus/audio_encoder_opus.cc b/api/audio_codecs/opus/audio_encoder_opus.cc index 6d950c5e74..5b6322da4c 100644 --- a/api/audio_codecs/opus/audio_encoder_opus.cc +++ b/api/audio_codecs/opus/audio_encoder_opus.cc @@ -32,7 +32,8 @@ AudioCodecInfo AudioEncoderOpus::QueryAudioEncoder( std::unique_ptr AudioEncoderOpus::MakeAudioEncoder( const AudioEncoderOpusConfig& config, int payload_type, - absl::optional /*codec_pair_id*/) { + absl::optional /*codec_pair_id*/, + const FieldTrialsView* field_trials) { if (!config.IsOk()) { RTC_DCHECK_NOTREACHED(); return nullptr; diff --git a/api/audio_codecs/opus/audio_encoder_opus.h b/api/audio_codecs/opus/audio_encoder_opus.h index 03cb0d6b38..df93ae5303 100644 --- a/api/audio_codecs/opus/audio_encoder_opus.h +++ b/api/audio_codecs/opus/audio_encoder_opus.h @@ -19,6 +19,7 @@ #include "api/audio_codecs/audio_encoder.h" #include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus/audio_encoder_opus_config.h" +#include "api/field_trials_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -34,7 +35,8 @@ struct RTC_EXPORT AudioEncoderOpus { static std::unique_ptr MakeAudioEncoder( const AudioEncoderOpusConfig& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr); }; } // namespace webrtc diff --git a/api/audio_codecs/opus_audio_encoder_factory.cc b/api/audio_codecs/opus_audio_encoder_factory.cc index 5f0c7147f5..8c286f21e1 100644 --- a/api/audio_codecs/opus_audio_encoder_factory.cc +++ b/api/audio_codecs/opus_audio_encoder_factory.cc @@ -37,8 +37,10 @@ struct NotAdvertised { static std::unique_ptr MakeAudioEncoder( const Config& config, int payload_type, - absl::optional codec_pair_id = absl::nullopt) { - return T::MakeAudioEncoder(config, payload_type, codec_pair_id); + absl::optional codec_pair_id = absl::nullopt, + const FieldTrialsView* field_trials = nullptr) { + return T::MakeAudioEncoder(config, payload_type, codec_pair_id, + field_trials); } }; diff --git a/api/audio_codecs/test/BUILD.gn b/api/audio_codecs/test/BUILD.gn index 575f062ce7..12df649feb 100644 --- a/api/audio_codecs/test/BUILD.gn +++ b/api/audio_codecs/test/BUILD.gn @@ -21,8 +21,8 @@ if (rtc_include_tests) { ] deps = [ "..:audio_codecs_api", - "../../../rtc_base:rtc_base_approved", "../../../test:audio_codec_mocks", + "../../../test:scoped_key_value_config", "../../../test:test_support", "../L16:audio_decoder_L16", "../L16:audio_encoder_L16", diff --git a/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc b/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc index 464ecfd487..3662f3b76d 100644 --- a/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc @@ -22,6 +22,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_decoder.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -77,9 +78,11 @@ struct AudioDecoderFakeApi { } // namespace TEST(AudioDecoderFactoryTemplateTest, NoDecoderTypes) { + test::ScopedKeyValueConfig field_trials; rtc::scoped_refptr factory( rtc::make_ref_counted< - audio_decoder_factory_template_impl::AudioDecoderFactoryT<>>()); + audio_decoder_factory_template_impl::AudioDecoderFactoryT<>>( + &field_trials)); EXPECT_THAT(factory->GetSupportedDecoders(), ::testing::IsEmpty()); EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1})); EXPECT_EQ(nullptr, diff --git a/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc b/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc index 110f9930bd..67b6883583 100644 --- a/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc @@ -22,6 +22,7 @@ #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -77,9 +78,11 @@ struct AudioEncoderFakeApi { } // namespace TEST(AudioEncoderFactoryTemplateTest, NoEncoderTypes) { + test::ScopedKeyValueConfig field_trials; rtc::scoped_refptr factory( rtc::make_ref_counted< - audio_encoder_factory_template_impl::AudioEncoderFactoryT<>>()); + audio_encoder_factory_template_impl::AudioEncoderFactoryT<>>( + &field_trials)); EXPECT_THAT(factory->GetSupportedEncoders(), ::testing::IsEmpty()); EXPECT_EQ(absl::nullopt, factory->QueryAudioEncoder({"foo", 8000, 1})); EXPECT_EQ(nullptr, diff --git a/api/audio_options.cc b/api/audio_options.cc index fad35cb881..c301395f94 100644 --- a/api/audio_options.cc +++ b/api/audio_options.cc @@ -54,8 +54,6 @@ void AudioOptions::SetAll(const AudioOptions& change) { change.audio_jitter_buffer_min_delay_ms); SetFrom(&audio_jitter_buffer_enable_rtx_handling, change.audio_jitter_buffer_enable_rtx_handling); - SetFrom(&typing_detection, change.typing_detection); - SetFrom(&residual_echo_detector, change.residual_echo_detector); SetFrom(&combined_audio_video_bwe, change.combined_audio_video_bwe); SetFrom(&audio_network_adaptor, change.audio_network_adaptor); SetFrom(&audio_network_adaptor_config, change.audio_network_adaptor_config); @@ -78,8 +76,6 @@ bool AudioOptions::operator==(const AudioOptions& o) const { o.audio_jitter_buffer_min_delay_ms && audio_jitter_buffer_enable_rtx_handling == o.audio_jitter_buffer_enable_rtx_handling && - typing_detection == o.typing_detection && - residual_echo_detector == o.residual_echo_detector && combined_audio_video_bwe == o.combined_audio_video_bwe && audio_network_adaptor == o.audio_network_adaptor && audio_network_adaptor_config == o.audio_network_adaptor_config && @@ -107,8 +103,6 @@ std::string AudioOptions::ToString() const { audio_jitter_buffer_min_delay_ms); ToStringIfSet(&result, "audio_jitter_buffer_enable_rtx_handling", audio_jitter_buffer_enable_rtx_handling); - ToStringIfSet(&result, "typing", typing_detection); - ToStringIfSet(&result, "residual_echo_detector", residual_echo_detector); ToStringIfSet(&result, "combined_audio_video_bwe", combined_audio_video_bwe); ToStringIfSet(&result, "audio_network_adaptor", audio_network_adaptor); ToStringIfSet(&result, "init_recording_on_send", init_recording_on_send); diff --git a/api/audio_options.h b/api/audio_options.h index 3fcc38d83f..1e4c820597 100644 --- a/api/audio_options.h +++ b/api/audio_options.h @@ -60,14 +60,6 @@ struct RTC_EXPORT AudioOptions { absl::optional audio_jitter_buffer_min_delay_ms; // Audio receiver jitter buffer (NetEq) should handle retransmitted packets. absl::optional audio_jitter_buffer_enable_rtx_handling; - // Deprecated. - // TODO(bugs.webrtc.org/11226): Remove. - // Audio processing to detect typing. - absl::optional typing_detection; - // TODO(bugs.webrtc.org/11539): Deprecated, replaced by - // webrtc::CreateEchoDetector() and injection when creating the audio - // processing module. - absl::optional residual_echo_detector; // Enable combined audio+bandwidth BWE. // TODO(pthatcher): This flag is set from the // "googCombinedAudioVideoBwe", but not used anywhere. So delete it, diff --git a/api/call/audio_sink.h b/api/call/audio_sink.h index fa4c3f6814..76feb13fee 100644 --- a/api/call/audio_sink.h +++ b/api/call/audio_sink.h @@ -11,13 +11,7 @@ #ifndef API_CALL_AUDIO_SINK_H_ #define API_CALL_AUDIO_SINK_H_ -#if defined(WEBRTC_POSIX) && !defined(__STDC_FORMAT_MACROS) -// Avoid conflict with format_macros.h. -#define __STDC_FORMAT_MACROS -#endif - -#include -#include +#include namespace webrtc { diff --git a/api/candidate.h b/api/candidate.h index ecfdee3fcb..b8aaebc14a 100644 --- a/api/candidate.h +++ b/api/candidate.h @@ -109,6 +109,13 @@ class RTC_EXPORT Candidate { network_type_ = network_type; } + rtc::AdapterType underlying_type_for_vpn() const { + return underlying_type_for_vpn_; + } + void set_underlying_type_for_vpn(rtc::AdapterType network_type) { + underlying_type_for_vpn_ = network_type; + } + // Candidates in a new generation replace those in the old generation. uint32_t generation() const { return generation_; } void set_generation(uint32_t generation) { generation_ = generation; } @@ -195,6 +202,7 @@ class RTC_EXPORT Candidate { std::string type_; std::string network_name_; rtc::AdapterType network_type_; + rtc::AdapterType underlying_type_for_vpn_; uint32_t generation_; std::string foundation_; rtc::SocketAddress related_address_; diff --git a/api/create_peerconnection_factory.cc b/api/create_peerconnection_factory.cc index c41b6d6fb2..4a01b2f3c9 100644 --- a/api/create_peerconnection_factory.cc +++ b/api/create_peerconnection_factory.cc @@ -38,7 +38,8 @@ rtc::scoped_refptr CreatePeerConnectionFactory( std::unique_ptr video_decoder_factory, rtc::scoped_refptr audio_mixer, rtc::scoped_refptr audio_processing, - AudioFrameProcessor* audio_frame_processor) { + AudioFrameProcessor* audio_frame_processor, + std::unique_ptr field_trials) { PeerConnectionFactoryDependencies dependencies; dependencies.network_thread = network_thread; dependencies.worker_thread = worker_thread; @@ -47,7 +48,11 @@ rtc::scoped_refptr CreatePeerConnectionFactory( dependencies.call_factory = CreateCallFactory(); dependencies.event_log_factory = std::make_unique( dependencies.task_queue_factory.get()); - dependencies.trials = std::make_unique(); + if (field_trials) { + dependencies.trials = std::move(field_trials); + } else { + dependencies.trials = std::make_unique(); + } if (network_thread) { // TODO(bugs.webrtc.org/13145): Add an rtc::SocketFactory* argument. diff --git a/api/create_peerconnection_factory.h b/api/create_peerconnection_factory.h index 4eb0a00e54..efebc5f3ea 100644 --- a/api/create_peerconnection_factory.h +++ b/api/create_peerconnection_factory.h @@ -49,7 +49,8 @@ CreatePeerConnectionFactory( std::unique_ptr video_decoder_factory, rtc::scoped_refptr audio_mixer, rtc::scoped_refptr audio_processing, - AudioFrameProcessor* audio_frame_processor = nullptr); + AudioFrameProcessor* audio_frame_processor = nullptr, + std::unique_ptr field_trials = nullptr); } // namespace webrtc diff --git a/api/field_trials.cc b/api/field_trials.cc new file mode 100644 index 0000000000..e6ca18b1cf --- /dev/null +++ b/api/field_trials.cc @@ -0,0 +1,92 @@ +/* + * 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 "api/field_trials.h" + +#include + +#include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" + +namespace { + +// This part is copied from system_wrappers/field_trial.cc. +webrtc::flat_map InsertIntoMap(const std::string& s) { + std::string::size_type field_start = 0; + webrtc::flat_map key_value_map; + while (field_start < s.size()) { + std::string::size_type separator_pos = s.find('/', field_start); + RTC_CHECK_NE(separator_pos, std::string::npos) + << "Missing separator '/' after field trial key."; + RTC_CHECK_GT(separator_pos, field_start) + << "Field trial key cannot be empty."; + std::string key = s.substr(field_start, separator_pos - field_start); + field_start = separator_pos + 1; + + RTC_CHECK_LT(field_start, s.size()) + << "Missing value after field trial key. String ended."; + separator_pos = s.find('/', field_start); + RTC_CHECK_NE(separator_pos, std::string::npos) + << "Missing terminating '/' in field trial string."; + RTC_CHECK_GT(separator_pos, field_start) + << "Field trial value cannot be empty."; + std::string value = s.substr(field_start, separator_pos - field_start); + field_start = separator_pos + 1; + + // If a key is specified multiple times, only the value linked to the first + // key is stored. note: This will crash in debug build when calling + // InitFieldTrialsFromString(). + key_value_map.emplace(key, value); + } + // This check is technically redundant due to earlier checks. + // We nevertheless keep the check to make it clear that the entire + // string has been processed, and without indexing past the end. + RTC_CHECK_EQ(field_start, s.size()); + + return key_value_map; +} + +// Makes sure that only one instance is created, since the usage +// of global string makes behaviour unpredicatable otherwise. +// TODO(bugs.webrtc.org/10335): Remove once global string is gone. +std::atomic instance_created_{false}; + +} // namespace + +namespace webrtc { + +FieldTrials::FieldTrials(const std::string& s) + : field_trial_string_(s), + previous_field_trial_string_(webrtc::field_trial::GetFieldTrialString()), + key_value_map_(InsertIntoMap(s)) { + // TODO(bugs.webrtc.org/10335): Remove the global string! + field_trial::InitFieldTrialsFromString(field_trial_string_.c_str()); + RTC_CHECK(!instance_created_.exchange(true)) + << "Only one instance may be instanciated at any given time!"; +} + +FieldTrials::~FieldTrials() { + // TODO(bugs.webrtc.org/10335): Remove the global string! + field_trial::InitFieldTrialsFromString(previous_field_trial_string_); + RTC_CHECK(instance_created_.exchange(false)); +} + +std::string FieldTrials::Lookup(absl::string_view key) const { + auto it = key_value_map_.find(std::string(key)); + if (it != key_value_map_.end()) + return it->second; + + // Check the global string so that programs using + // a mix between FieldTrials and the global string continue to work + // TODO(bugs.webrtc.org/10335): Remove the global string! + return field_trial::FindFullName(std::string(key)); +} + +} // namespace webrtc diff --git a/api/field_trials.h b/api/field_trials.h new file mode 100644 index 0000000000..822e78c096 --- /dev/null +++ b/api/field_trials.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 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_FIELD_TRIALS_H_ +#define API_FIELD_TRIALS_H_ + +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" +#include "rtc_base/containers/flat_map.h" + +namespace webrtc { + +// The FieldTrials class is used to inject field trials into webrtc. +// +// Field trials allow webrtc clients (such as Chromium) to turn on feature code +// in binaries out in the field and gather information with that. +// +// They are designed to be easy to use with Chromium field trials and to speed +// up developers by reducing the need to wire up APIs to control whether a +// feature is on/off. +// +// The field trials are injected into objects that use them at creation time. +// +// NOTE: Creating multiple FieldTrials-object is currently prohibited +// until we remove the global string (TODO(bugs.webrtc.org/10335)) +class FieldTrials : public FieldTrialsView { + public: + explicit FieldTrials(const std::string& s); + ~FieldTrials(); + + std::string Lookup(absl::string_view key) const override; + + private: + const std::string field_trial_string_; + const char* const previous_field_trial_string_; + const flat_map key_value_map_; +}; + +} // namespace webrtc + +#endif // API_FIELD_TRIALS_H_ diff --git a/api/field_trials_unittest.cc b/api/field_trials_unittest.cc new file mode 100644 index 0000000000..fb2fff0db7 --- /dev/null +++ b/api/field_trials_unittest.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2022 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/field_trials.h" + +#include "api/transport/field_trial_based_config.h" +#include "system_wrappers/include/field_trial.h" +#include "test/gtest.h" + +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +#include "test/testsupport/rtc_expect_death.h" +#endif // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +namespace webrtc { + +TEST(FieldTrials, EmptyString) { + FieldTrials f(""); + EXPECT_FALSE(f.IsEnabled("MyCoolTrial")); + EXPECT_FALSE(f.IsDisabled("MyCoolTrial")); +} + +TEST(FieldTrials, EnableDisable) { + FieldTrials f("MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"); + EXPECT_TRUE(f.IsEnabled("MyCoolTrial")); + EXPECT_TRUE(f.IsDisabled("MyUncoolTrial")); +} + +TEST(FieldTrials, SetGlobalStringAndReadFromFieldTrial) { + const char* s = "MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"; + webrtc::field_trial::InitFieldTrialsFromString(s); + FieldTrialBasedConfig f; + EXPECT_TRUE(f.IsEnabled("MyCoolTrial")); + EXPECT_TRUE(f.IsDisabled("MyUncoolTrial")); +} + +TEST(FieldTrials, SetFieldTrialAndReadFromGlobalString) { + FieldTrials f("MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"); + EXPECT_TRUE(webrtc::field_trial::IsEnabled("MyCoolTrial")); + EXPECT_TRUE(webrtc::field_trial::IsDisabled("MyUncoolTrial")); +} + +TEST(FieldTrials, RestoresGlobalString) { + const char* s = "SomeString/Enabled/"; + webrtc::field_trial::InitFieldTrialsFromString(s); + { + FieldTrials f("SomeOtherString/Enabled/"); + EXPECT_EQ(std::string("SomeOtherString/Enabled/"), + webrtc::field_trial::GetFieldTrialString()); + } + EXPECT_EQ(s, webrtc::field_trial::GetFieldTrialString()); +} + +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +TEST(FieldTrials, OnlyOneInstance) { + FieldTrials f("SomeString/Enabled/"); + RTC_EXPECT_DEATH(FieldTrials("SomeOtherString/Enabled/").Lookup("Whatever"), + "Only one instance"); +} +#endif // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +TEST(FieldTrials, SequentialInstances) { + { FieldTrials f("SomeString/Enabled/"); } + { FieldTrials f("SomeOtherString/Enabled/"); } +} + +} // namespace webrtc diff --git a/api/transport/webrtc_key_value_config.h b/api/field_trials_view.h similarity index 67% rename from api/transport/webrtc_key_value_config.h rename to api/field_trials_view.h index 5666a82783..299205d1d3 100644 --- a/api/transport/webrtc_key_value_config.h +++ b/api/field_trials_view.h @@ -7,8 +7,8 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#ifndef API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ -#define API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ +#ifndef API_FIELD_TRIALS_VIEW_H_ +#define API_FIELD_TRIALS_VIEW_H_ #include @@ -22,12 +22,25 @@ namespace webrtc { // particular key value mapping will be preserved over time and no announcements // will be made if they are changed. It's up to the library user to ensure that // the behavior does not break. -class RTC_EXPORT WebRtcKeyValueConfig { +class RTC_EXPORT FieldTrialsView { public: - virtual ~WebRtcKeyValueConfig() = default; + virtual ~FieldTrialsView() = default; // The configured value for the given key. Defaults to an empty string. virtual std::string Lookup(absl::string_view key) const = 0; + + bool IsEnabled(absl::string_view key) const { + return Lookup(key).find("Enabled") == 0; + } + + bool IsDisabled(absl::string_view key) const { + return Lookup(key).find("Disabled") == 0; + } }; + +// TODO(bugs.webrtc.org/10335): Remove once all migrated to +// api/field_trials_view.h +typedef FieldTrialsView WebRtcKeyValueConfig; + } // namespace webrtc -#endif // API_TRANSPORT_WEBRTC_KEY_VALUE_CONFIG_H_ +#endif // API_FIELD_TRIALS_VIEW_H_ diff --git a/api/ice_transport_factory.cc b/api/ice_transport_factory.cc index 26ef88bf1c..9e7e629a6f 100644 --- a/api/ice_transport_factory.cc +++ b/api/ice_transport_factory.cc @@ -58,18 +58,9 @@ rtc::scoped_refptr CreateIceTransport( rtc::scoped_refptr CreateIceTransport( IceTransportInit init) { - if (init.async_resolver_factory()) { - // Backwards compatibility mode - return rtc::make_ref_counted( - std::make_unique( - "", cricket::ICE_CANDIDATE_COMPONENT_RTP, init.port_allocator(), - init.async_resolver_factory(), init.event_log())); - } else { - return rtc::make_ref_counted( - cricket::P2PTransportChannel::Create( - "", cricket::ICE_CANDIDATE_COMPONENT_RTP, init.port_allocator(), - init.async_dns_resolver_factory(), init.event_log())); - } + return rtc::make_ref_counted( + cricket::P2PTransportChannel::Create( + "", cricket::ICE_CANDIDATE_COMPONENT_RTP, std::move(init))); } } // namespace webrtc diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h index a3b364c87a..9d8bd5abe3 100644 --- a/api/ice_transport_interface.h +++ b/api/ice_transport_interface.h @@ -23,9 +23,11 @@ namespace cricket { class IceTransportInternal; class PortAllocator; +class IceControllerFactoryInterface; } // namespace cricket namespace webrtc { +class FieldTrialsView; // An ICE transport, as represented to the outside world. // This object is refcounted, and is therefore alive until the @@ -74,12 +76,27 @@ struct IceTransportInit final { RtcEventLog* event_log() { return event_log_; } void set_event_log(RtcEventLog* event_log) { event_log_ = event_log; } + void set_ice_controller_factory( + cricket::IceControllerFactoryInterface* ice_controller_factory) { + ice_controller_factory_ = ice_controller_factory; + } + cricket::IceControllerFactoryInterface* ice_controller_factory() { + return ice_controller_factory_; + } + + const FieldTrialsView* field_trials() { return field_trials_; } + void set_field_trials(const FieldTrialsView* field_trials) { + field_trials_ = field_trials; + } + private: cricket::PortAllocator* port_allocator_ = nullptr; AsyncDnsResolverFactoryInterface* async_dns_resolver_factory_ = nullptr; // For backwards compatibility. Only one resolver factory can be set. AsyncResolverFactory* async_resolver_factory_ = nullptr; RtcEventLog* event_log_ = nullptr; + cricket::IceControllerFactoryInterface* ice_controller_factory_ = nullptr; + const FieldTrialsView* field_trials_ = nullptr; // TODO(https://crbug.com/webrtc/12657): Redesign to have const members. }; diff --git a/api/media_stream_interface.h b/api/media_stream_interface.h index 7e010289a0..9d336739e4 100644 --- a/api/media_stream_interface.h +++ b/api/media_stream_interface.h @@ -334,11 +334,38 @@ class MediaStreamInterface : public rtc::RefCountInterface, const std::string& track_id) = 0; // Takes ownership of added tracks. - // TODO(hta): Should take scoped_refptr rather than raw pointer. - virtual bool AddTrack(AudioTrackInterface* track) = 0; - virtual bool AddTrack(VideoTrackInterface* track) = 0; - virtual bool RemoveTrack(AudioTrackInterface* track) = 0; - virtual bool RemoveTrack(VideoTrackInterface* track) = 0; + // Note: Default implementations are for avoiding link time errors in + // implementations that mock this API. + // TODO(bugs.webrtc.org/13980): Remove default implementations. + virtual bool AddTrack(rtc::scoped_refptr track) { + RTC_CHECK_NOTREACHED(); + } + virtual bool AddTrack(rtc::scoped_refptr track) { + RTC_CHECK_NOTREACHED(); + } + virtual bool RemoveTrack(rtc::scoped_refptr track) { + RTC_CHECK_NOTREACHED(); + } + virtual bool RemoveTrack(rtc::scoped_refptr track) { + RTC_CHECK_NOTREACHED(); + } + // Deprecated: Should use scoped_refptr versions rather than pointers. + [[deprecated("Pass a scoped_refptr")]] virtual bool AddTrack( + AudioTrackInterface* track) { + return AddTrack(rtc::scoped_refptr(track)); + } + [[deprecated("Pass a scoped_refptr")]] virtual bool AddTrack( + VideoTrackInterface* track) { + return AddTrack(rtc::scoped_refptr(track)); + } + [[deprecated("Pass a scoped_refptr")]] virtual bool RemoveTrack( + AudioTrackInterface* track) { + return RemoveTrack(rtc::scoped_refptr(track)); + } + [[deprecated("Pass a scoped_refptr")]] virtual bool RemoveTrack( + VideoTrackInterface* track) { + return RemoveTrack(rtc::scoped_refptr(track)); + } protected: ~MediaStreamInterface() override = default; diff --git a/api/neteq/BUILD.gn b/api/neteq/BUILD.gn index 4e85c4d268..504fa059bb 100644 --- a/api/neteq/BUILD.gn +++ b/api/neteq/BUILD.gn @@ -20,7 +20,7 @@ rtc_source_set("neteq_api") { "..:rtp_headers", "..:rtp_packet_info", "..:scoped_refptr", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", "../../system_wrappers:system_wrappers", "../audio_codecs:audio_codecs_api", ] @@ -54,7 +54,6 @@ rtc_source_set("neteq_controller_api") { deps = [ ":neteq_api", ":tick_timer", - "../../rtc_base:rtc_base_approved", "../../system_wrappers:system_wrappers", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -81,7 +80,6 @@ rtc_source_set("tick_timer") { ] deps = [ "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] } diff --git a/api/neteq/neteq.h b/api/neteq/neteq.h index 675742a1ce..aed18faefc 100644 --- a/api/neteq/neteq.h +++ b/api/neteq/neteq.h @@ -88,6 +88,8 @@ struct NetEqLifetimeStatistics { // these events. int32_t interruption_count = 0; int32_t total_interruption_duration_ms = 0; + // Total number of comfort noise samples generated during DTX. + uint64_t generated_noise_samples = 0; }; // Metrics that describe the operations performed in NetEq, and the internal diff --git a/api/notifier.h b/api/notifier.h index c03b1049eb..fc2480e00a 100644 --- a/api/notifier.h +++ b/api/notifier.h @@ -14,7 +14,9 @@ #include #include "api/media_stream_interface.h" +#include "api/sequence_checker.h" #include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" namespace webrtc { @@ -23,14 +25,16 @@ namespace webrtc { template class Notifier : public T { public: - Notifier() {} + Notifier() { sequence_checker_.Detach(); } virtual void RegisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); RTC_DCHECK(observer != nullptr); observers_.push_back(observer); } virtual void UnregisterObserver(ObserverInterface* observer) { + RTC_DCHECK_RUN_ON(&sequence_checker_); for (std::list::iterator it = observers_.begin(); it != observers_.end(); it++) { if (*it == observer) { @@ -41,6 +45,7 @@ class Notifier : public T { } void FireOnChanged() { + RTC_DCHECK_RUN_ON(&sequence_checker_); // Copy the list of observers to avoid a crash if the observer object // unregisters as a result of the OnChanged() call. If the same list is used // UnregisterObserver will affect the list make the iterator invalid. @@ -52,7 +57,10 @@ class Notifier : public T { } protected: - std::list observers_; + std::list observers_ RTC_GUARDED_BY(sequence_checker_); + + private: + RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_; }; } // namespace webrtc diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index 4c4a638ad5..c326799edb 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -91,6 +91,7 @@ #include "api/data_channel_interface.h" #include "api/dtls_transport_interface.h" #include "api/fec_controller.h" +#include "api/field_trials_view.h" #include "api/ice_transport_interface.h" #include "api/jsep.h" #include "api/media_stream_interface.h" @@ -117,7 +118,6 @@ #include "api/transport/enums.h" #include "api/transport/network_control.h" #include "api/transport/sctp_transport_factory_interface.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/turn_customizer.h" #include "api/video/video_bitrate_allocator_factory.h" #include "call/rtp_transport_controller_send_factory_interface.h" @@ -428,8 +428,8 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { ////////////////////////////////////////////////////////////////////////// // If set to true, don't gather IPv6 ICE candidates. - // TODO(deadbeef): Remove this? IPv6 support has long stopped being - // experimental + // TODO(https://crbug.com/1315576): Remove the ability to set it in Chromium + // and delete this flag. bool disable_ipv6 = false; // If set to true, don't gather IPv6 ICE candidates on Wi-Fi. @@ -457,11 +457,14 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // Use new combined audio/video bandwidth estimation? absl::optional combined_audio_video_bwe; +#if defined(WEBRTC_FUCHSIA) + // TODO(bugs.webrtc.org/11066): Remove entirely once Fuchsia does not use. // TODO(bugs.webrtc.org/9891) - Move to crypto_options // Can be used to disable DTLS-SRTP. This should never be done, but can be // useful for testing purposes, for example in setting up a loopback call // with a single PeerConnection. absl::optional enable_dtls_srtp; +#endif ///////////////////////////////////////////////// // The below fields are not part of the standard. @@ -1395,6 +1398,9 @@ struct RTC_EXPORT PeerConnectionDependencies final { std::unique_ptr tls_cert_verifier; std::unique_ptr video_bitrate_allocator_factory; + // Optional field trials to use. + // Overrides those from PeerConnectionFactoryDependencies. + std::unique_ptr trials; }; // PeerConnectionFactoryDependencies holds all of the PeerConnectionFactory @@ -1435,7 +1441,7 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final { std::unique_ptr network_monitor_factory; std::unique_ptr neteq_factory; std::unique_ptr sctp_factory; - std::unique_ptr trials; + std::unique_ptr trials; std::unique_ptr transport_controller_send_factory; std::unique_ptr metronome; diff --git a/api/scoped_refptr.h b/api/scoped_refptr.h index 8f45f89206..e145509127 100644 --- a/api/scoped_refptr.h +++ b/api/scoped_refptr.h @@ -104,7 +104,7 @@ class scoped_refptr { } T* get() const { return ptr_; } - operator T*() const { return ptr_; } + explicit operator bool() const { return ptr_ != nullptr; } T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } @@ -161,6 +161,62 @@ class scoped_refptr { T* ptr_; }; +template +bool operator==(const rtc::scoped_refptr& a, + const rtc::scoped_refptr& b) { + return a.get() == b.get(); +} +template +bool operator!=(const rtc::scoped_refptr& a, + const rtc::scoped_refptr& b) { + return !(a == b); +} + +template +bool operator==(const rtc::scoped_refptr& a, std::nullptr_t) { + return a.get() == nullptr; +} + +template +bool operator!=(const rtc::scoped_refptr& a, std::nullptr_t) { + return !(a == nullptr); +} + +template +bool operator==(std::nullptr_t, const rtc::scoped_refptr& a) { + return a.get() == nullptr; +} + +template +bool operator!=(std::nullptr_t, const rtc::scoped_refptr& a) { + return !(a == nullptr); +} + +// Comparison with raw pointer. +template +bool operator==(const rtc::scoped_refptr& a, const U* b) { + return a.get() == b; +} +template +bool operator!=(const rtc::scoped_refptr& a, const U* b) { + return !(a == b); +} + +template +bool operator==(const T* a, const rtc::scoped_refptr& b) { + return a == b.get(); +} +template +bool operator!=(const T* a, const rtc::scoped_refptr& b) { + return !(a == b); +} + +// Ordered comparison, needed for use as a std::map key. +template +bool operator<(const rtc::scoped_refptr& a, const rtc::scoped_refptr& b) { + return a.get() < b.get(); +} + } // namespace rtc #endif // API_SCOPED_REFPTR_H_ diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index f38d962e15..14c5f24a2c 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -90,6 +90,31 @@ struct RTCContentType { static const char* const kScreenshare; }; +// https://w3c.github.io/webrtc-stats/#dom-rtcdtlsrole +struct RTCDtlsRole { + static const char* const kUnknown; + static const char* const kClient; + static const char* const kServer; +}; + +// https://www.w3.org/TR/webrtc/#rtcicerole +struct RTCIceRole { + static const char* const kUnknown; + static const char* const kControlled; + static const char* const kControlling; +}; + +// https://www.w3.org/TR/webrtc/#dom-rtcicetransportstate +struct RTCIceTransportState { + static const char* const kNew; + static const char* const kChecking; + static const char* const kConnected; + static const char* const kCompleted; + static const char* const kDisconnected; + static const char* const kFailed; + static const char* const kClosed; +}; + // https://w3c.github.io/webrtc-stats/#certificatestats-dict* class RTC_EXPORT RTCCertificateStats final : public RTCStats { public: @@ -106,6 +131,20 @@ class RTC_EXPORT RTCCertificateStats final : public RTCStats { RTCStatsMember issuer_certificate_id; }; +// Non standard extension mapping to rtc::AdapterType +struct RTCNetworkAdapterType { + static constexpr char kUnknown[] = "unknown"; + static constexpr char kEthernet[] = "ethernet"; + static constexpr char kWifi[] = "wifi"; + static constexpr char kCellular[] = "cellular"; + static constexpr char kLoopback[] = "loopback"; + static constexpr char kAny[] = "any"; + static constexpr char kCellular2g[] = "cellular2g"; + static constexpr char kCellular3g[] = "cellular3g"; + static constexpr char kCellular4g[] = "cellular4g"; + static constexpr char kCellular5g[] = "cellular5g"; +}; + // https://w3c.github.io/webrtc-stats/#codec-dict* class RTC_EXPORT RTCCodecStats final : public RTCStats { public: @@ -227,6 +266,9 @@ class RTC_EXPORT RTCIceCandidateStats : public RTCStats { RTCStatsMember priority; RTCStatsMember url; + RTCNonStandardStatsMember vpn; + RTCNonStandardStatsMember network_adapter_type; + protected: RTCIceCandidateStats(const std::string& id, int64_t timestamp_us, @@ -463,7 +505,6 @@ class RTC_EXPORT RTCInboundRTPStreamStats final RTCStatsMember audio_level; RTCStatsMember total_audio_energy; RTCStatsMember total_samples_duration; - RTCStatsMember frames_received; // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 RTCStatsMember round_trip_time; // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 @@ -484,6 +525,8 @@ class RTC_EXPORT RTCInboundRTPStreamStats final RTCStatsMember gap_loss_rate; // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 RTCStatsMember gap_discard_rate; + // Stats below are only implemented or defined for video. + RTCStatsMember frames_received; RTCStatsMember frame_width; RTCStatsMember frame_height; RTCStatsMember frame_bit_depth; @@ -492,6 +535,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final RTCStatsMember key_frames_decoded; RTCStatsMember frames_dropped; RTCStatsMember total_decode_time; + RTCStatsMember total_processing_delay; RTCStatsMember total_inter_frame_delay; RTCStatsMember total_squared_inter_frame_delay; // https://henbos.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype @@ -501,7 +545,7 @@ class RTC_EXPORT RTCInboundRTPStreamStats final // TODO(hbos): This is only implemented for video; implement it for audio as // well. RTCStatsMember decoder_implementation; - // FIR and PLI counts are only defined for |media_type == "video"|. + // FIR and PLI counts are only defined for |kind == "video"|. RTCStatsMember fir_count; RTCStatsMember pli_count; RTCStatsMember nack_count; @@ -552,7 +596,7 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { // TODO(hbos): This is only implemented for video; implement it for audio as // well. RTCStatsMember encoder_implementation; - // FIR and PLI counts are only defined for |media_type == "video"|. + // FIR and PLI counts are only defined for |kind == "video"|. RTCStatsMember fir_count; RTCStatsMember pli_count; RTCStatsMember nack_count; @@ -672,8 +716,12 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats { RTCStatsMember remote_certificate_id; RTCStatsMember tls_version; RTCStatsMember dtls_cipher; + RTCStatsMember dtls_role; RTCStatsMember srtp_cipher; RTCStatsMember selected_candidate_pair_changes; + RTCStatsMember ice_role; + RTCStatsMember ice_local_username_fragment; + RTCStatsMember ice_state; }; } // namespace webrtc diff --git a/api/stats_types.cc b/api/stats_types.cc index b044e4ab11..8f69e5f876 100644 --- a/api/stats_types.cc +++ b/api/stats_types.cc @@ -648,9 +648,6 @@ const char* StatsReport::Value::display_name() const { return "googTrackId"; case kStatsValueNameTimingFrameInfo: return "googTimingFrameInfo"; - // TODO(bugs.webrtc.org/11226): Remove. - case kStatsValueNameTypingNoiseState: - return "googTypingNoiseState"; case kStatsValueNameWritable: return "googWritable"; case kStatsValueNameAudioDeviceUnderrunCounter: diff --git a/api/stats_types.h b/api/stats_types.h index e7dd528e62..d75da46439 100644 --- a/api/stats_types.h +++ b/api/stats_types.h @@ -235,8 +235,6 @@ class RTC_EXPORT StatsReport { kStatsValueNameTrackId, kStatsValueNameTransmitBitrate, kStatsValueNameTransportType, - // TODO(bugs.webrtc.org/11226): Remove. - kStatsValueNameTypingNoiseState, kStatsValueNameWritable, kStatsValueNameAudioDeviceUnderrunCounter, kStatsValueNameLocalCandidateRelayProtocol, diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h index b7c92f8647..c3e79b72cc 100644 --- a/api/task_queue/task_queue_base.h +++ b/api/task_queue/task_queue_base.h @@ -130,7 +130,7 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { bool IsCurrent() const { return Current() == this; } protected: - class CurrentTaskQueueSetter { + class RTC_EXPORT CurrentTaskQueueSetter { public: explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue); CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete; diff --git a/api/test/DEPS b/api/test/DEPS index 6c3a9ebed1..48889d528f 100644 --- a/api/test/DEPS +++ b/api/test/DEPS @@ -27,6 +27,7 @@ specific_include_rules = { "+rtc_base/ssl_certificate.h", "+rtc_base/thread.h", "+media/base/media_constants.h", + "+modules/audio_processing/include/audio_processing.h", ], "time_controller\.h": [ "+modules/utility/include/process_thread.h", diff --git a/api/test/compile_all_headers.cc b/api/test/compile_all_headers.cc index 9275eb0791..b80c565843 100644 --- a/api/test/compile_all_headers.cc +++ b/api/test/compile_all_headers.cc @@ -34,6 +34,7 @@ #include "api/test/mock_audio_mixer.h" #include "api/test/mock_audio_sink.h" #include "api/test/mock_data_channel.h" +#include "api/test/mock_dtmf_sender.h" #include "api/test/mock_frame_decryptor.h" #include "api/test/mock_frame_encryptor.h" #include "api/test/mock_media_stream_interface.h" diff --git a/api/test/fake_frame_decryptor.h b/api/test/fake_frame_decryptor.h index bfd0e6903b..cb58dd6c99 100644 --- a/api/test/fake_frame_decryptor.h +++ b/api/test/fake_frame_decryptor.h @@ -27,8 +27,7 @@ namespace webrtc { // FrameDecryptorInterface. It is constructed with a simple single digit key and // a fixed postfix byte. This is just to validate that the core code works // as expected. -class FakeFrameDecryptor final - : public rtc::RefCountedObject { +class FakeFrameDecryptor : public FrameDecryptorInterface { public: // Provide a key (0,255) and some postfix byte (0,255) this should match the // byte you expect from the FakeFrameEncryptor. diff --git a/api/test/mock_dtmf_sender.h b/api/test/mock_dtmf_sender.h new file mode 100644 index 0000000000..9029195025 --- /dev/null +++ b/api/test/mock_dtmf_sender.h @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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_TEST_MOCK_DTMF_SENDER_H_ +#define API_TEST_MOCK_DTMF_SENDER_H_ + +#include + +#include "api/dtmf_sender_interface.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockDtmfSenderObserver : public DtmfSenderObserverInterface { + public: + MOCK_METHOD(void, + OnToneChange, + (const std::string&, const std::string&), + (override)); + MOCK_METHOD(void, OnToneChange, (const std::string&), (override)); +}; + +static_assert(!std::is_abstract_v, ""); + +class MockDtmfSender : public DtmfSenderInterface { + public: + static rtc::scoped_refptr Create() { + return rtc::make_ref_counted(); + } + + MOCK_METHOD(void, + RegisterObserver, + (DtmfSenderObserverInterface * observer), + (override)); + MOCK_METHOD(void, UnregisterObserver, (), (override)); + MOCK_METHOD(bool, CanInsertDtmf, (), (override)); + MOCK_METHOD(std::string, tones, (), (const override)); + MOCK_METHOD(int, duration, (), (const override)); + MOCK_METHOD(int, inter_tone_gap, (), (const override)); + + protected: + MockDtmfSender() = default; +}; + +static_assert(!std::is_abstract_v>, ""); + +} // namespace webrtc + +#endif // API_TEST_MOCK_DTMF_SENDER_H_ diff --git a/api/test/mock_media_stream_interface.h b/api/test/mock_media_stream_interface.h index 17a30a877e..209962358d 100644 --- a/api/test/mock_media_stream_interface.h +++ b/api/test/mock_media_stream_interface.h @@ -84,6 +84,52 @@ class MockAudioTrack final : public rtc::RefCountedObject { MockAudioTrack() = default; }; +class MockMediaStream : public MediaStreamInterface { + public: + MOCK_METHOD(std::string, id, (), (const override)); + MOCK_METHOD(AudioTrackVector, GetAudioTracks, (), (override)); + MOCK_METHOD(VideoTrackVector, GetVideoTracks, (), (override)); + MOCK_METHOD(rtc::scoped_refptr, + FindAudioTrack, + (const std::string& track_id), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + FindVideoTrack, + (const std::string& track_id), + (override)); + MOCK_METHOD(bool, + AddTrack, + (rtc::scoped_refptr track), + (override)); + MOCK_METHOD(bool, + AddTrack, + (rtc::scoped_refptr track), + (override)); + MOCK_METHOD(bool, + RemoveTrack, + (rtc::scoped_refptr track), + (override)); + MOCK_METHOD(bool, + RemoveTrack, + (rtc::scoped_refptr track), + (override)); + // Old AddTrack/RemoveTrack methods - slated for removal + MOCK_METHOD(bool, AddTrack, (AudioTrackInterface * track), (override)); + MOCK_METHOD(bool, AddTrack, (VideoTrackInterface * track), (override)); + MOCK_METHOD(bool, RemoveTrack, (AudioTrackInterface * track), (override)); + MOCK_METHOD(bool, RemoveTrack, (VideoTrackInterface * track), (override)); + MOCK_METHOD(void, + RegisterObserver, + (ObserverInterface * observer), + (override)); + MOCK_METHOD(void, + UnregisterObserver, + (ObserverInterface * observer), + (override)); +}; + +static_assert(!std::is_abstract_v>, ""); + } // namespace webrtc #endif // API_TEST_MOCK_MEDIA_STREAM_INTERFACE_H_ diff --git a/api/test/mock_peerconnectioninterface.h b/api/test/mock_peerconnectioninterface.h index effd24e294..97b2b7d7b1 100644 --- a/api/test/mock_peerconnectioninterface.h +++ b/api/test/mock_peerconnectioninterface.h @@ -25,8 +25,7 @@ namespace webrtc { -class MockPeerConnectionInterface - : public rtc::RefCountedObject { +class MockPeerConnectionInterface : public webrtc::PeerConnectionInterface { public: static rtc::scoped_refptr Create() { return rtc::make_ref_counted(); @@ -199,7 +198,9 @@ class MockPeerConnectionInterface MOCK_METHOD(void, Close, (), (override)); }; -static_assert(!std::is_abstract::value, ""); +static_assert( + !std::is_abstract_v>, + ""); } // namespace webrtc diff --git a/api/test/mock_rtp_transceiver.h b/api/test/mock_rtp_transceiver.h index 5ea9028b77..1d21bce5eb 100644 --- a/api/test/mock_rtp_transceiver.h +++ b/api/test/mock_rtp_transceiver.h @@ -19,11 +19,12 @@ namespace webrtc { -class MockRtpTransceiver final - : public rtc::RefCountedObject { +class MockRtpTransceiver : public RtpTransceiverInterface { public: + MockRtpTransceiver() = default; + static rtc::scoped_refptr Create() { - return rtc::scoped_refptr(new MockRtpTransceiver()); + return rtc::make_ref_counted(); } MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); @@ -79,9 +80,6 @@ class MockRtpTransceiver final (rtc::ArrayView header_extensions_to_offer), (override)); - - private: - MockRtpTransceiver() = default; }; } // namespace webrtc diff --git a/api/test/mock_rtpsender.h b/api/test/mock_rtpsender.h index e4d6399eed..e36eec4618 100644 --- a/api/test/mock_rtpsender.h +++ b/api/test/mock_rtpsender.h @@ -19,8 +19,12 @@ namespace webrtc { -class MockRtpSender : public rtc::RefCountedObject { +class MockRtpSender : public RtpSenderInterface { public: + static rtc::scoped_refptr Create() { + return rtc::make_ref_counted(); + } + MOCK_METHOD(bool, SetTrack, (MediaStreamTrackInterface*), (override)); MOCK_METHOD(rtc::scoped_refptr, track, @@ -42,6 +46,7 @@ class MockRtpSender : public rtc::RefCountedObject { (const, override)); }; +static_assert(!std::is_abstract_v>, ""); } // namespace webrtc #endif // API_TEST_MOCK_RTPSENDER_H_ diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index a8044d7230..f9f26eb5d4 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -21,8 +21,8 @@ rtc_library("network_emulation") { "../..:array_view", "../../../rtc_base", "../../../rtc_base:checks", + "../../../rtc_base:copy_on_write_buffer", "../../../rtc_base:ip_address", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:socket_address", "../../numerics", "../../task_queue", diff --git a/api/test/peerconnection_quality_test_fixture.cc b/api/test/peerconnection_quality_test_fixture.cc new file mode 100644 index 0000000000..59526f9f52 --- /dev/null +++ b/api/test/peerconnection_quality_test_fixture.cc @@ -0,0 +1,85 @@ +/* + * Copyright 2022 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/test/peerconnection_quality_test_fixture.h" + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +using VideoCodecConfig = ::webrtc::webrtc_pc_e2e:: + PeerConnectionE2EQualityTestFixture::VideoCodecConfig; +using VideoSubscription = ::webrtc::webrtc_pc_e2e:: + PeerConnectionE2EQualityTestFixture::VideoSubscription; + +PeerConnectionE2EQualityTestFixture::VideoResolution::VideoResolution( + size_t width, + size_t height, + int32_t fps) + : width_(width), height_(height), fps_(fps), spec_(Spec::kNone) {} +PeerConnectionE2EQualityTestFixture::VideoResolution::VideoResolution(Spec spec) + : width_(0), height_(0), fps_(0), spec_(spec) {} + +bool PeerConnectionE2EQualityTestFixture::VideoResolution::operator==( + const VideoResolution& other) const { + if (spec_ != Spec::kNone && spec_ == other.spec_) { + // If there is some particular spec set, then it doesn't matter what + // values we have in other fields. + return true; + } + return width_ == other.width_ && height_ == other.height_ && + fps_ == other.fps_ && spec_ == other.spec_; +} + +absl::optional +PeerConnectionE2EQualityTestFixture::VideoSubscription::GetMaxResolution( + rtc::ArrayView video_configs) { + std::vector resolutions; + for (const auto& video_config : video_configs) { + resolutions.push_back(video_config.GetResolution()); + } + return GetMaxResolution(resolutions); +} + +absl::optional +PeerConnectionE2EQualityTestFixture::VideoSubscription::GetMaxResolution( + rtc::ArrayView resolutions) { + if (resolutions.empty()) { + return absl::nullopt; + } + + VideoResolution max_resolution; + for (const VideoResolution& resolution : resolutions) { + if (max_resolution.width() < resolution.width()) { + max_resolution.set_width(resolution.width()); + } + if (max_resolution.height() < resolution.height()) { + max_resolution.set_height(resolution.height()); + } + if (max_resolution.fps() < resolution.fps()) { + max_resolution.set_fps(resolution.fps()); + } + } + return max_resolution; +} + +PeerConnectionE2EQualityTestFixture::VideoConfig::VideoConfig( + const VideoResolution& resolution) + : width(resolution.width()), + height(resolution.height()), + fps(resolution.fps()) { + RTC_CHECK(resolution.IsRegular()); +} + +} // namespace webrtc_pc_e2e +} // namespace webrtc diff --git a/api/test/peerconnection_quality_test_fixture.h b/api/test/peerconnection_quality_test_fixture.h index 198868d7a7..fee911a8b8 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -19,7 +19,9 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "api/array_view.h" #include "api/async_resolver_factory.h" +#include "api/audio/audio_mixer.h" #include "api/call/call_factory_interface.h" #include "api/fec_controller.h" #include "api/function_view.h" @@ -41,6 +43,7 @@ #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/base/media_constants.h" +#include "modules/audio_processing/include/audio_processing.h" #include "rtc_base/network.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_certificate.h" @@ -124,7 +127,12 @@ class PeerConnectionE2EQualityTestFixture { std::vector slides_yuv_file_names; }; - // Config for Vp8 simulcast or Vp9 SVC testing. + // Config for Vp8 simulcast or non-standard Vp9 SVC testing. + // + // To configure standard SVC setting, use `scalability_mode` in the + // `encoding_params` array. + // This configures Vp9 SVC by requesting simulcast layers, the request is + // internally converted to a request for SVC layers. // // SVC support is limited: // During SVC testing there is no SFU, so framework will try to emulate SFU @@ -166,18 +174,50 @@ class PeerConnectionE2EQualityTestFixture { // It requires Selective Forwarding Unit (SFU) to be configured in the // network. absl::optional target_spatial_index; + }; - // Encoding parameters per simulcast layer. If not empty, `encoding_params` - // size have to be equal to `simulcast_streams_count`. Will be used to set - // transceiver send encoding params for simulcast layers. Applicable only - // for codecs that support simulcast (ex. Vp8) and will be ignored - // otherwise. RtpEncodingParameters::rid may be changed by fixture - // implementation to ensure signaling correctness. - std::vector encoding_params; + class VideoResolution { + public: + // Determines special resolutions, which can't be expressed in terms of + // width, height and fps. + enum class Spec { + // No extra spec set. It describes a regular resolution described by + // width, height and fps. + kNone, + // Describes resolution which contains max value among all sender's + // video streams in each dimension (width, height, fps). + kMaxFromSender + }; + + VideoResolution(size_t width, size_t height, int32_t fps); + explicit VideoResolution(Spec spec = Spec::kNone); + + bool operator==(const VideoResolution& other) const; + bool operator!=(const VideoResolution& other) const { + return !(*this == other); + } + + size_t width() const { return width_; } + void set_width(size_t width) { width_ = width; } + size_t height() const { return height_; } + void set_height(size_t height) { height_ = height; } + int32_t fps() const { return fps_; } + void set_fps(int32_t fps) { fps_ = fps; } + + // Returns if it is a regular resolution or not. The resolution is regular + // if it's spec is `Spec::kNone`. + bool IsRegular() const { return spec_ == Spec::kNone; } + + private: + size_t width_ = 0; + size_t height_ = 0; + int32_t fps_ = 0; + Spec spec_ = Spec::kNone; }; // Contains properties of single video stream. struct VideoConfig { + explicit VideoConfig(const VideoResolution& resolution); VideoConfig(size_t width, size_t height, int32_t fps) : width(width), height(height), fps(fps) {} VideoConfig(std::string stream_label, @@ -190,10 +230,14 @@ class PeerConnectionE2EQualityTestFixture { stream_label(std::move(stream_label)) {} // Video stream width. - const size_t width; + size_t width; // Video stream height. - const size_t height; - const int32_t fps; + size_t height; + int32_t fps; + VideoResolution GetResolution() const { + return VideoResolution(width, height, fps); + } + // Have to be unique among all specified configs for all peers in the call. // Will be auto generated if omitted. absl::optional stream_label; @@ -209,21 +253,18 @@ class PeerConnectionE2EQualityTestFixture { // but only on non-lossy networks. See more in documentation to // VideoSimulcastConfig. absl::optional simulcast_config; + // Encoding parameters for both singlecast and per simulcast layer. + // If singlecast is used, if not empty, a single value can be provided. + // If simulcast is used, if not empty, `encoding_params` size have to be + // equal to `simulcast_config.simulcast_streams_count`. Will be used to set + // transceiver send encoding params for each layer. + // RtpEncodingParameters::rid may be changed by fixture implementation to + // ensure signaling correctness. + std::vector encoding_params; // Count of temporal layers for video stream. This value will be set into // each RtpEncodingParameters of RtpParameters of corresponding // RtpSenderInterface for this video stream. absl::optional temporal_layers_count; - // Sets the maximum encode bitrate in bps. If this value is not set, the - // encoder will be capped at an internal maximum value around 2 Mbps - // depending on the resolution. This means that it will never be able to - // utilize a high bandwidth link. - absl::optional max_encode_bitrate_bps; - // Sets the minimum encode bitrate in bps. If this value is not set, the - // encoder will use an internal minimum value. Please note that if this - // value is set higher than the bandwidth of the link, the encoder will - // generate more data than the link can handle regardless of the bandwidth - // estimation. - absl::optional min_encode_bitrate_bps; // If specified the input stream will be also copied to specified file. // It is actually one of the test's output file, which contains copy of what // was captured during the test for this video stream on sender side. @@ -325,6 +366,68 @@ class PeerConnectionE2EQualityTestFixture { std::map required_params; }; + // Subscription to the remote video streams. It declares which remote stream + // peer should receive and in which resolution (width x height x fps). + class VideoSubscription { + public: + // Returns the resolution constructed as maximum from all resolution + // dimensions: width, height and fps. + static absl::optional GetMaxResolution( + rtc::ArrayView video_configs); + static absl::optional GetMaxResolution( + rtc::ArrayView resolutions); + + // Subscribes receiver to all streams sent by the specified peer with + // specified resolution. It will override any resolution that was used in + // `SubscribeToAll` independently from methods call order. + VideoSubscription& SubscribeToPeer( + absl::string_view peer_name, + VideoResolution resolution = + VideoResolution(VideoResolution::Spec::kMaxFromSender)) { + peers_resolution_[std::string(peer_name)] = resolution; + return *this; + } + + // Subscribes receiver to the all sent streams with specified resolution. + // If any stream was subscribed to with `SubscribeTo` method that will + // override resolution passed to this function independently from methods + // call order. + VideoSubscription& SubscribeToAllPeers( + VideoResolution resolution = + VideoResolution(VideoResolution::Spec::kMaxFromSender)) { + default_resolution_ = resolution; + return *this; + } + + // Returns resolution for specific sender. If no specific resolution was + // set for this sender, then will return resolution used for all streams. + // If subscription doesn't subscribe to all streams, `absl::nullopt` will be + // returned. + absl::optional GetResolutionForPeer( + absl::string_view peer_name) const { + auto it = peers_resolution_.find(std::string(peer_name)); + if (it == peers_resolution_.end()) { + return default_resolution_; + } + return it->second; + } + + // Returns a maybe empty list of senders for which peer explicitly + // subscribed to with specific resolution. + std::vector GetSubscribedPeers() const { + std::vector subscribed_streams; + subscribed_streams.reserve(peers_resolution_.size()); + for (const auto& entry : peers_resolution_) { + subscribed_streams.push_back(entry.first); + } + return subscribed_streams; + } + + private: + absl::optional default_resolution_ = absl::nullopt; + std::map peers_resolution_; + }; + // This class is used to fully configure one peer inside the call. class PeerConfigurer { public: @@ -357,6 +460,10 @@ class PeerConnectionE2EQualityTestFixture { // Set a custom NetEqFactory to be used in the call. virtual PeerConfigurer* SetNetEqFactory( std::unique_ptr neteq_factory) = 0; + virtual PeerConfigurer* SetAudioProcessing( + rtc::scoped_refptr audio_processing) = 0; + virtual PeerConfigurer* SetAudioMixer( + rtc::scoped_refptr audio_mixer) = 0; // The parameters of the following 4 methods will be passed to the // PeerConnectionInterface implementation that will be created for this @@ -371,6 +478,11 @@ class PeerConnectionE2EQualityTestFixture { std::unique_ptr tls_cert_verifier) = 0; virtual PeerConfigurer* SetIceTransportFactory( std::unique_ptr factory) = 0; + // Flags to set on `cricket::PortAllocator`. These flags will be added + // to the default ones that are presented on the port allocator. + // For possible values check p2p/base/port_allocator.h. + virtual PeerConfigurer* SetPortAllocatorExtraFlags( + uint32_t extra_flags) = 0; // Add new video stream to the call that will be sent from this peer. // Default implementation of video frames generator will be used. @@ -385,6 +497,11 @@ class PeerConnectionE2EQualityTestFixture { virtual PeerConfigurer* AddVideoConfig( VideoConfig config, CapturingDeviceIndex capturing_device_index) = 0; + // Sets video subscription for the peer. By default subscription will + // include all streams with `VideoSubscription::kSameAsSendStream` + // resolution. To override this behavior use this method. + virtual PeerConfigurer* SetVideoSubscription( + VideoSubscription subscription) = 0; // Set the list of video codecs used by the peer during the test. These // codecs will be negotiated in SDP during offer/answer exchange. The order // of these codecs during negotiation will be the same as in `video_codecs`. @@ -420,6 +537,8 @@ class PeerConnectionE2EQualityTestFixture { virtual PeerConfigurer* SetAecDumpPath(std::string path) = 0; virtual PeerConfigurer* SetRTCConfiguration( PeerConnectionInterface::RTCConfiguration configuration) = 0; + virtual PeerConfigurer* SetRTCOfferAnswerOptions( + PeerConnectionInterface::RTCOfferAnswerOptions options) = 0; // Set bitrate parameters on PeerConnection. This constraints will be // applied to all summed RTP streams for this peer. virtual PeerConfigurer* SetBitrateSettings( diff --git a/api/test/peerconnection_quality_test_fixture_unittest.cc b/api/test/peerconnection_quality_test_fixture_unittest.cc new file mode 100644 index 0000000000..c0e739cc23 --- /dev/null +++ b/api/test/peerconnection_quality_test_fixture_unittest.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2022 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/test/peerconnection_quality_test_fixture.h" + +#include + +#include "absl/types/optional.h" +#include "rtc_base/gunit.h" +#include "test/gmock.h" + +namespace webrtc { +namespace webrtc_pc_e2e { +namespace { + +using VideoResolution = ::webrtc::webrtc_pc_e2e:: + PeerConnectionE2EQualityTestFixture::VideoResolution; +using VideoConfig = + ::webrtc::webrtc_pc_e2e::PeerConnectionE2EQualityTestFixture::VideoConfig; +using VideoSubscription = ::webrtc::webrtc_pc_e2e:: + PeerConnectionE2EQualityTestFixture::VideoSubscription; + +TEST(PclfVideoSubscription, MaxFromSenderSpecEqualIndependentOfOtherFields) { + VideoResolution r1(VideoResolution::Spec::kMaxFromSender); + r1.set_width(1); + r1.set_height(2); + r1.set_fps(3); + VideoResolution r2(VideoResolution::Spec::kMaxFromSender); + r1.set_width(4); + r1.set_height(5); + r1.set_fps(6); + EXPECT_EQ(r1, r2); +} + +TEST(PclfVideoSubscription, WhenSpecIsNotSetFieldsAreCompared) { + VideoResolution test_resolution(/*width=*/1, /*height=*/2, + /*fps=*/3); + VideoResolution equal_resolution(/*width=*/1, /*height=*/2, + /*fps=*/3); + VideoResolution different_width(/*width=*/10, /*height=*/2, + /*fps=*/3); + VideoResolution different_height(/*width=*/1, /*height=*/20, + /*fps=*/3); + VideoResolution different_fps(/*width=*/1, /*height=*/20, + /*fps=*/30); + + EXPECT_EQ(test_resolution, equal_resolution); + EXPECT_NE(test_resolution, different_width); + EXPECT_NE(test_resolution, different_height); + EXPECT_NE(test_resolution, different_fps); +} + +TEST(PclfVideoSubscription, GetMaxResolutionForEmptyReturnsNullopt) { + absl::optional resolution = + VideoSubscription::GetMaxResolution(std::vector{}); + ASSERT_FALSE(resolution.has_value()); +} + +TEST(PclfVideoSubscription, GetMaxResolutionSelectMaxForEachDimention) { + VideoConfig max_width(/*width=*/1000, /*height=*/1, /*fps=*/1); + VideoConfig max_height(/*width=*/1, /*height=*/100, /*fps=*/1); + VideoConfig max_fps(/*width=*/1, /*height=*/1, /*fps=*/10); + + absl::optional resolution = + VideoSubscription::GetMaxResolution( + std::vector{max_width, max_height, max_fps}); + ASSERT_TRUE(resolution.has_value()); + EXPECT_EQ(resolution->width(), static_cast(1000)); + EXPECT_EQ(resolution->height(), static_cast(100)); + EXPECT_EQ(resolution->fps(), 10); +} + +} // namespace +} // namespace webrtc_pc_e2e +} // namespace webrtc diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn index 30955273b0..44d4b117b5 100644 --- a/api/transport/BUILD.gn +++ b/api/transport/BUILD.gn @@ -32,7 +32,7 @@ rtc_library("network_control") { ] deps = [ - ":webrtc_key_value_config", + "../../api:field_trials_view", "../rtc_event_log", "../units:data_rate", "../units:data_size", @@ -45,13 +45,6 @@ rtc_library("network_control") { ] } -rtc_source_set("webrtc_key_value_config") { - visibility = [ "*" ] - sources = [ "webrtc_key_value_config.h" ] - deps = [ "../../rtc_base/system:rtc_export" ] - absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] -} - rtc_library("field_trial_based_config") { visibility = [ "*" ] sources = [ @@ -59,7 +52,7 @@ rtc_library("field_trial_based_config") { "field_trial_based_config.h", ] deps = [ - ":webrtc_key_value_config", + "../../api:field_trials_view", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] @@ -72,7 +65,7 @@ rtc_source_set("datagram_transport_interface") { deps = [ "..:array_view", "..:rtc_error", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:copy_on_write_buffer", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -85,8 +78,8 @@ rtc_library("goog_cc") { ] deps = [ ":network_control", - ":webrtc_key_value_config", "..:network_state_predictor_api", + "../../api:field_trials_view", "../../modules/congestion_controller/goog_cc", ] absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] @@ -106,10 +99,12 @@ rtc_source_set("stun_types") { deps = [ "../../api:array_view", + "../../rtc_base:byte_buffer", + "../../rtc_base:byte_order", "../../rtc_base:checks", "../../rtc_base:ip_address", + "../../rtc_base:logging", "../../rtc_base:rtc_base", - "../../rtc_base:rtc_base_approved", "../../rtc_base:socket_address", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] @@ -147,7 +142,9 @@ if (rtc_include_tests) { deps = [ ":stun_types", "../../rtc_base", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:byte_buffer", + "../../rtc_base:byte_order", + "../../rtc_base:macromagic", "../../rtc_base:socket_address", "../../test:test_support", "//testing/gtest", diff --git a/api/transport/field_trial_based_config.h b/api/transport/field_trial_based_config.h index 0754570fde..f0063ff95e 100644 --- a/api/transport/field_trial_based_config.h +++ b/api/transport/field_trial_based_config.h @@ -13,11 +13,11 @@ #include #include "absl/strings/string_view.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" namespace webrtc { // Implementation using the field trial API fo the key value lookup. -class FieldTrialBasedConfig : public WebRtcKeyValueConfig { +class FieldTrialBasedConfig : public FieldTrialsView { public: std::string Lookup(absl::string_view key) const override; }; diff --git a/api/transport/network_control.h b/api/transport/network_control.h index c2b005e713..862322443d 100644 --- a/api/transport/network_control.h +++ b/api/transport/network_control.h @@ -15,9 +15,9 @@ #include #include "absl/base/attributes.h" +#include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" namespace webrtc { @@ -46,7 +46,7 @@ struct NetworkControllerConfig { // Optional override of configuration of WebRTC internals. Using nullptr here // indicates that the field trial API will be used. - const WebRtcKeyValueConfig* key_value_config = nullptr; + const FieldTrialsView* key_value_config = nullptr; // Optional override of event log. RtcEventLog* event_log = nullptr; }; @@ -132,7 +132,7 @@ class NetworkStateEstimator { class NetworkStateEstimatorFactory { public: virtual std::unique_ptr Create( - const WebRtcKeyValueConfig* key_value_config) = 0; + const FieldTrialsView* key_value_config) = 0; virtual ~NetworkStateEstimatorFactory() = default; }; } // namespace webrtc diff --git a/api/units/time_delta.h b/api/units/time_delta.h index 6f1910379b..d5951005e3 100644 --- a/api/units/time_delta.h +++ b/api/units/time_delta.h @@ -32,6 +32,11 @@ namespace webrtc { // microseconds (us). class TimeDelta final : public rtc_units_impl::RelativeUnit { public: + template + static constexpr TimeDelta Minutes(T value) { + static_assert(std::is_arithmetic::value, ""); + return Seconds(value * 60); + } template static constexpr TimeDelta Seconds(T value) { static_assert(std::is_arithmetic::value, ""); diff --git a/api/units/time_delta_unittest.cc b/api/units/time_delta_unittest.cc index cb43860531..51a7aa233a 100644 --- a/api/units/time_delta_unittest.cc +++ b/api/units/time_delta_unittest.cc @@ -28,10 +28,12 @@ TEST(TimeDeltaTest, ConstExpr) { static_assert(kTimeDeltaPlusInf > kTimeDeltaZero, ""); + constexpr TimeDelta kTimeDeltaMinutes = TimeDelta::Minutes(kValue); constexpr TimeDelta kTimeDeltaSeconds = TimeDelta::Seconds(kValue); constexpr TimeDelta kTimeDeltaMs = TimeDelta::Millis(kValue); constexpr TimeDelta kTimeDeltaUs = TimeDelta::Micros(kValue); + static_assert(kTimeDeltaMinutes.seconds_or(0) == kValue * 60, ""); static_assert(kTimeDeltaSeconds.seconds_or(0) == kValue, ""); static_assert(kTimeDeltaMs.ms_or(0) == kValue, ""); static_assert(kTimeDeltaUs.us_or(0) == kValue, ""); @@ -54,10 +56,12 @@ TEST(TimeDeltaTest, GetDifferentPrefix) { EXPECT_EQ(TimeDelta::Micros(kValue).seconds(), kValue / 1000000); EXPECT_EQ(TimeDelta::Millis(kValue).seconds(), kValue / 1000); EXPECT_EQ(TimeDelta::Micros(kValue).ms(), kValue / 1000); + EXPECT_EQ(TimeDelta::Minutes(kValue / 60).seconds(), kValue); EXPECT_EQ(TimeDelta::Millis(kValue).us(), kValue * 1000); EXPECT_EQ(TimeDelta::Seconds(kValue).ms(), kValue * 1000); EXPECT_EQ(TimeDelta::Seconds(kValue).us(), kValue * 1000000); + EXPECT_EQ(TimeDelta::Minutes(kValue / 60).seconds(), kValue); } TEST(TimeDeltaTest, IdentityChecks) { @@ -183,14 +187,6 @@ TEST(TimeDeltaTest, MathOperations) { EXPECT_EQ((delta_a + delta_b).ms(), kValueA + kValueB); EXPECT_EQ((delta_a - delta_b).ms(), kValueA - kValueB); - const int32_t kInt32Value = 123; - const double kFloatValue = 123.0; - EXPECT_EQ((TimeDelta::Micros(kValueA) * kValueB).us(), kValueA * kValueB); - EXPECT_EQ((TimeDelta::Micros(kValueA) * kInt32Value).us(), - kValueA * kInt32Value); - EXPECT_EQ((TimeDelta::Micros(kValueA) * kFloatValue).us(), - kValueA * kFloatValue); - EXPECT_EQ((delta_b / 10).ms(), kValueB / 10); EXPECT_EQ(delta_b / delta_a, static_cast(kValueB) / kValueA); @@ -204,6 +200,26 @@ TEST(TimeDeltaTest, MathOperations) { EXPECT_EQ(mutable_delta, TimeDelta::Millis(kValueA)); } +TEST(TimeDeltaTest, MultiplyByScalar) { + const TimeDelta kValue = TimeDelta::Micros(267); + const int64_t kInt64 = 450; + const int32_t kInt32 = 123; + const size_t kUnsignedInt = 125; + const double kFloat = 123.0; + + EXPECT_EQ((kValue * kInt64).us(), kValue.us() * kInt64); + EXPECT_EQ(kValue * kInt64, kInt64 * kValue); + + EXPECT_EQ((kValue * kInt32).us(), kValue.us() * kInt32); + EXPECT_EQ(kValue * kInt32, kInt32 * kValue); + + EXPECT_EQ((kValue * kUnsignedInt).us(), kValue.us() * int64_t{kUnsignedInt}); + EXPECT_EQ(kValue * kUnsignedInt, kUnsignedInt * kValue); + + EXPECT_DOUBLE_EQ((kValue * kFloat).us(), kValue.us() * kFloat); + EXPECT_EQ(kValue * kFloat, kFloat * kValue); +} + TEST(TimeDeltaTest, InfinityOperations) { const int64_t kValue = 267; const TimeDelta finite = TimeDelta::Millis(kValue); diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn index 9fd28914b2..6057a7386d 100644 --- a/api/video/BUILD.gn +++ b/api/video/BUILD.gn @@ -28,9 +28,12 @@ rtc_library("video_rtp_headers") { deps = [ "..:array_view", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", "../units:data_rate", + "../units:time_delta", ] absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector", @@ -43,6 +46,8 @@ rtc_library("video_frame") { sources = [ "i420_buffer.cc", "i420_buffer.h", + "i422_buffer.cc", + "i422_buffer.h", "i444_buffer.cc", "i444_buffer.h", "nv12_buffer.cc", @@ -64,7 +69,8 @@ rtc_library("video_frame") { "..:scoped_refptr", "..:video_track_source_constraints", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", + "../../rtc_base:timeutils", "../../rtc_base/memory:aligned_malloc", "../../rtc_base/system:rtc_export", "//third_party/libyuv", @@ -90,6 +96,7 @@ rtc_library("video_frame_i010") { "..:scoped_refptr", "../../rtc_base", "../../rtc_base:checks", + "../../rtc_base:refcount", "../../rtc_base/memory:aligned_malloc", "//third_party/libyuv", ] @@ -135,7 +142,7 @@ rtc_library("encoded_image") { "..:rtp_packet_info", "..:scoped_refptr", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", "../../rtc_base/system:rtc_export", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -148,7 +155,11 @@ rtc_library("encoded_frame") { "encoded_frame.h", ] - deps = [ "../../modules/video_coding:encoded_frame" ] + deps = [ + "../../modules/video_coding:encoded_frame", + "../units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("rtp_video_frame_assembler") { @@ -233,7 +244,6 @@ rtc_source_set("video_bitrate_allocator_factory") { sources = [ "video_bitrate_allocator_factory.h" ] deps = [ ":video_bitrate_allocator", - "../../rtc_base:rtc_base_approved", "../video_codecs:video_codecs_api", ] } @@ -262,7 +272,7 @@ rtc_library("video_stream_decoder_create") { deps = [ ":video_stream_decoder", - "../../rtc_base:rtc_base_approved", + "../../api:field_trials_view", "../../video:video_stream_decoder_impl", "../task_queue", "../video_codecs:video_codecs_api", @@ -347,6 +357,40 @@ rtc_library("builtin_video_bitrate_allocator_factory") { absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] } +rtc_library("frame_buffer") { + sources = [ + "frame_buffer.cc", + "frame_buffer.h", + ] + deps = [ + "../../api:field_trials_view", + "../../api/units:timestamp", + "../../api/video:encoded_frame", + "../../modules/video_coding:video_coding_utility", + "../../rtc_base:logging", + "../../rtc_base:rtc_numerics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/container:inlined_vector", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("frame_buffer_unittest") { + testonly = true + sources = [ "frame_buffer_unittest.cc" ] + + deps = [ + ":frame_buffer", + "../../api/video:encoded_frame", + "../../test:fake_encoded_frame", + "../../test:field_trial", + "../../test:scoped_key_value_config", + "../../test:test_support", + ] +} + if (rtc_include_tests) { rtc_library("video_unittests") { testonly = true diff --git a/api/video/DEPS b/api/video/DEPS index 5a3e496bcf..967b8e7f9d 100644 --- a/api/video/DEPS +++ b/api/video/DEPS @@ -18,6 +18,10 @@ specific_include_rules = { "+rtc_base/memory/aligned_malloc.h", ], + "i422_buffer\.h": [ + "+rtc_base/memory/aligned_malloc.h", + ], + "i444_buffer\.h": [ "+rtc_base/memory/aligned_malloc.h", ], @@ -48,4 +52,12 @@ specific_include_rules = { "rtp_video_frame_assembler.h": [ "+modules/rtp_rtcp/source/rtp_packet_received.h", ], + + "frame_buffer.h": [ + "+modules/video_coding/utility/decoded_frames_history.h", + ], + + "video_frame_matchers\.h": [ + "+test/gmock.h", + ], } diff --git a/api/video/encoded_frame.cc b/api/video/encoded_frame.cc index 86d1a698a7..c5e2abbbb4 100644 --- a/api/video/encoded_frame.cc +++ b/api/video/encoded_frame.cc @@ -10,8 +10,22 @@ #include "api/video/encoded_frame.h" +#include "absl/types/optional.h" + namespace webrtc { +absl::optional EncodedFrame::ReceivedTimestamp() const { + return ReceivedTime() >= 0 + ? absl::make_optional(Timestamp::Millis(ReceivedTime())) + : absl::nullopt; +} + +absl::optional EncodedFrame::RenderTimestamp() const { + return RenderTimeMs() >= 0 + ? absl::make_optional(Timestamp::Millis(RenderTimeMs())) + : absl::nullopt; +} + bool EncodedFrame::delayed_by_retransmission() const { return false; } diff --git a/api/video/encoded_frame.h b/api/video/encoded_frame.h index 3ef26caf6e..66aee227bb 100644 --- a/api/video/encoded_frame.h +++ b/api/video/encoded_frame.h @@ -14,6 +14,8 @@ #include #include +#include "absl/types/optional.h" +#include "api/units/timestamp.h" #include "modules/video_coding/encoded_frame.h" namespace webrtc { @@ -30,10 +32,18 @@ class EncodedFrame : public webrtc::VCMEncodedFrame { virtual ~EncodedFrame() {} // When this frame was received. + // TODO(bugs.webrtc.org/13756): Use Timestamp instead of int. virtual int64_t ReceivedTime() const = 0; + // Returns a Timestamp from `ReceivedTime`, or nullopt if there is no receive + // time. + absl::optional ReceivedTimestamp() const; // When this frame should be rendered. + // TODO(bugs.webrtc.org/13756): Use Timestamp instead of int. virtual int64_t RenderTime() const = 0; + // Returns a Timestamp from `RenderTime`, or nullopt if there is no + // render time. + absl::optional RenderTimestamp() const; // This information is currently needed by the timing calculation class. // TODO(philipel): Remove this function when a new timing class has diff --git a/modules/video_coding/frame_buffer3.cc b/api/video/frame_buffer.cc similarity index 89% rename from modules/video_coding/frame_buffer3.cc rename to api/video/frame_buffer.cc index d02a99749a..67d5f6787f 100644 --- a/modules/video_coding/frame_buffer3.cc +++ b/api/video/frame_buffer.cc @@ -8,18 +8,14 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/video_coding/frame_buffer3.h" +#include "api/video/frame_buffer.h" #include -#include -#include -#include #include "absl/algorithm/container.h" #include "absl/container/inlined_vector.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/sequence_number_util.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -53,7 +49,7 @@ int64_t GetFrameId(const FrameIteratorT& it) { } template -int64_t GetTimestamp(const FrameIteratorT& it) { +uint32_t GetTimestamp(const FrameIteratorT& it) { return it->second.encoded_frame->Timestamp(); } @@ -63,9 +59,11 @@ bool IsLastFrameInTemporalUnit(const FrameIteratorT& it) { } } // namespace -FrameBuffer::FrameBuffer(int max_size, int max_decode_history) +FrameBuffer::FrameBuffer(int max_size, + int max_decode_history, + const FieldTrialsView& field_trials) : legacy_frame_id_jump_behavior_( - !field_trial::IsDisabled("WebRTC-LegacyFrameIdJumpBehavior")), + !field_trials.IsDisabled("WebRTC-LegacyFrameIdJumpBehavior")), max_size_(max_size), decoded_frame_history_(max_decode_history) {} @@ -157,17 +155,9 @@ absl::optional FrameBuffer::LastContinuousTemporalUnitFrameId() const { return last_continuous_temporal_unit_frame_id_; } -absl::optional FrameBuffer::NextDecodableTemporalUnitRtpTimestamp() - const { - if (!next_decodable_temporal_unit_) { - return absl::nullopt; - } - return GetTimestamp(next_decodable_temporal_unit_->first_frame); -} - -absl::optional FrameBuffer::LastDecodableTemporalUnitRtpTimestamp() - const { - return last_decodable_temporal_unit_timestamp_; +absl::optional +FrameBuffer::DecodableTemporalUnitsInfo() const { + return decodable_temporal_units_info_; } int FrameBuffer::GetTotalNumberOfContinuousTemporalUnits() const { @@ -220,7 +210,7 @@ void FrameBuffer::PropagateContinuity(const FrameIterator& frame_it) { void FrameBuffer::FindNextAndLastDecodableTemporalUnit() { next_decodable_temporal_unit_.reset(); - last_decodable_temporal_unit_timestamp_.reset(); + decodable_temporal_units_info_.reset(); if (!last_continuous_temporal_unit_frame_id_) { return; @@ -229,6 +219,7 @@ void FrameBuffer::FindNextAndLastDecodableTemporalUnit() { FrameIterator first_frame_it = frames_.begin(); FrameIterator last_frame_it = frames_.begin(); absl::InlinedVector frames_in_temporal_unit; + uint32_t last_decodable_temporal_unit_timestamp; for (auto frame_it = frames_.begin(); frame_it != frames_.end();) { if (GetFrameId(frame_it) > *last_continuous_temporal_unit_frame_id_) { break; @@ -263,16 +254,23 @@ void FrameBuffer::FindNextAndLastDecodableTemporalUnit() { next_decodable_temporal_unit_ = {first_frame_it, last_frame_it}; } - last_decodable_temporal_unit_timestamp_ = GetTimestamp(first_frame_it); + last_decodable_temporal_unit_timestamp = GetTimestamp(first_frame_it); } } } + + if (next_decodable_temporal_unit_) { + decodable_temporal_units_info_ = { + .next_rtp_timestamp = + GetTimestamp(next_decodable_temporal_unit_->first_frame), + .last_rtp_timestamp = last_decodable_temporal_unit_timestamp}; + } } void FrameBuffer::Clear() { frames_.clear(); next_decodable_temporal_unit_.reset(); - last_decodable_temporal_unit_timestamp_.reset(); + decodable_temporal_units_info_.reset(); last_continuous_frame_id_.reset(); last_continuous_temporal_unit_frame_id_.reset(); decoded_frame_history_.Clear(); diff --git a/modules/video_coding/frame_buffer3.h b/api/video/frame_buffer.h similarity index 85% rename from modules/video_coding/frame_buffer3.h rename to api/video/frame_buffer.h index 1f3f71a4a5..5ab88c50ee 100644 --- a/modules/video_coding/frame_buffer3.h +++ b/api/video/frame_buffer.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_VIDEO_CODING_FRAME_BUFFER3_H_ -#define MODULES_VIDEO_CODING_FRAME_BUFFER3_H_ +#ifndef API_VIDEO_FRAME_BUFFER_H_ +#define API_VIDEO_FRAME_BUFFER_H_ #include #include @@ -17,6 +17,7 @@ #include "absl/container/inlined_vector.h" #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/units/timestamp.h" #include "api/video/encoded_frame.h" #include "modules/video_coding/utility/decoded_frames_history.h" @@ -30,10 +31,18 @@ namespace webrtc { // The FrameBuffer is thread-unsafe. class FrameBuffer { public: + struct DecodabilityInfo { + uint32_t next_rtp_timestamp; + uint32_t last_rtp_timestamp; + }; + // The `max_size` determines the maxmimum number of frames the buffer will // store, and max_decode_history determines how far back (by frame ID) the // buffer will store if a frame was decoded or not. - FrameBuffer(int max_size, int max_decode_history); + FrameBuffer(int max_size, + int max_decode_history, + // TODO(hta): remove field trials! + const FieldTrialsView& field_trials); FrameBuffer(const FrameBuffer&) = delete; FrameBuffer& operator=(const FrameBuffer&) = delete; ~FrameBuffer() = default; @@ -52,8 +61,7 @@ class FrameBuffer { absl::optional LastContinuousFrameId() const; absl::optional LastContinuousTemporalUnitFrameId() const; - absl::optional NextDecodableTemporalUnitRtpTimestamp() const; - absl::optional LastDecodableTemporalUnitRtpTimestamp() const; + absl::optional DecodableTemporalUnitsInfo() const; int GetTotalNumberOfContinuousTemporalUnits() const; int GetTotalNumberOfDroppedFrames() const; @@ -83,7 +91,7 @@ class FrameBuffer { const size_t max_size_; FrameMap frames_; absl::optional next_decodable_temporal_unit_; - absl::optional last_decodable_temporal_unit_timestamp_; + absl::optional decodable_temporal_units_info_; absl::optional last_continuous_frame_id_; absl::optional last_continuous_temporal_unit_frame_id_; video_coding::DecodedFramesHistory decoded_frame_history_; @@ -94,4 +102,4 @@ class FrameBuffer { } // namespace webrtc -#endif // MODULES_VIDEO_CODING_FRAME_BUFFER3_H_ +#endif // API_VIDEO_FRAME_BUFFER_H_ diff --git a/api/video/frame_buffer_unittest.cc b/api/video/frame_buffer_unittest.cc new file mode 100644 index 0000000000..41a486f192 --- /dev/null +++ b/api/video/frame_buffer_unittest.cc @@ -0,0 +1,369 @@ +/* + * 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 "api/video/frame_buffer.h" + +#include + +#include "api/video/encoded_frame.h" +#include "test/fake_encoded_frame.h" +#include "test/field_trial.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" + +namespace webrtc { +namespace { + +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::Matches; + +MATCHER_P(FrameWithId, id, "") { + return Matches(Eq(id))(arg->Id()); +} + +TEST(FrameBuffer3Test, RejectInvalidRefs) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + // Ref must be less than the id of this frame. + buffer.InsertFrame( + test::FakeFrameBuilder().Time(0).Id(0).Refs({0}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); + + // Duplicate ids are also invalid. + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1, 1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); +} + +TEST(FrameBuffer3Test, LastContinuousUpdatesOnInsertedFrames) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(2)); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); +} + +TEST(FrameBuffer3Test, LastContinuousFrameReordering) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(3)); +} + +TEST(FrameBuffer3Test, LastContinuousTemporalUnit) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); +} + +TEST(FrameBuffer3Test, LastContinuousTemporalUnitReordering) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(3).Refs({1}).Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(4).Refs({2, 3}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(4)); +} + +TEST(FrameBuffer3Test, NextDecodable) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo(), Eq(absl::nullopt)); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(10U)); +} + +TEST(FrameBuffer3Test, AdvanceNextDecodableOnExtraction) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(2).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(10U)); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(20U)); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(2))); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(30U)); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, AdvanceLastDecodableOnExtraction) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->last_rtp_timestamp, Eq(10U)); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->last_rtp_timestamp, Eq(30U)); +} + +TEST(FrameBuffer3Test, FrameUpdatesNextDecodable) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(2).AsLast().Build()); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(20U)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(10U)); +} + +TEST(FrameBuffer3Test, KeyframeClearsFullBuffer) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/5, /*max_decode_history=*/10, + field_trials); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({2}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({3}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(50).Id(5).Refs({4}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); + + // Frame buffer is full + buffer.InsertFrame( + test::FakeFrameBuilder().Time(60).Id(6).Refs({5}).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(70).Id(7).AsLast().Build()); + EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(7)); +} + +TEST(FrameBuffer3Test, DropNextDecodableTemporalUnit) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + buffer.DropNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, OldFramesAreIgnored) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + buffer.ExtractNextDecodableTemporalUnit(); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build()); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); +} + +TEST(FrameBuffer3Test, ReturnFullTemporalUnitKSVC) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1), FrameWithId(2), FrameWithId(3))); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(4).Refs({3}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(4))); +} + +TEST(FrameBuffer3Test, InterleavedStream) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({2}).AsLast().Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(50).Id(5).Refs({3}).AsLast().Build()); + + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(2))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(4))); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(5))); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(70).Id(7).Refs({5}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(7))); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(60).Id(6).Refs({4}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(90).Id(9).Refs({7}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(9))); +} + +TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { + { + test::ScopedKeyValueConfig field_trials( + "WebRTC-LegacyFrameIdJumpBehavior/Disabled/"); + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(2).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + } + + { + // WebRTC-LegacyFrameIdJumpBehavior is disabled by default. + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(3))); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(2).Refs({1}).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), + ElementsAre(FrameWithId(1))); + } +} + +TEST(FrameBuffer3Test, TotalNumberOfContinuousTemporalUnits) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(0)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(40).Id(4).Refs({2}).Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(5).Refs({3, 4}).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); + + // Reordered + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).Refs({2}).AsLast().Build()); + EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(3)); +} + +TEST(FrameBuffer3Test, TotalNumberOfDroppedFrames) { + test::ScopedKeyValueConfig field_trials; + FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, + field_trials); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); + + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).Refs({2}).AsLast().Build()); + buffer.InsertFrame(test::FakeFrameBuilder().Time(40).Id(4).Refs({1}).Build()); + buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(5).Refs({4}).AsLast().Build()); + + buffer.ExtractNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); + + buffer.DropNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); + + buffer.ExtractNextDecodableTemporalUnit(); + EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); +} + +} // namespace +} // namespace webrtc diff --git a/api/video/i422_buffer.cc b/api/video/i422_buffer.cc new file mode 100644 index 0000000000..d6cf0d6c97 --- /dev/null +++ b/api/video/i422_buffer.cc @@ -0,0 +1,355 @@ +/* + * 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 "api/video/i422_buffer.h" + +#include + +#include +#include + +#include "api/video/i420_buffer.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/planar_functions.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +// Aligning pointer to 64 bytes for improved performance, e.g. use SIMD. +static const int kBufferAlignment = 64; + +namespace webrtc { + +namespace { + +int I422DataSize(int height, int stride_y, int stride_u, int stride_v) { + return stride_y * height + stride_u * height + stride_v * height; +} + +// TODO(sergio.garcia.murillo@gmail.com): Remove as soon it is available in +// libyuv. Due to the rotate&scale required, this function may not be merged in +// to libyuv inmediatelly. +// https://bugs.chromium.org/p/libyuv/issues/detail?id=926 +int webrtcI422Rotate(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height, + enum libyuv::RotationMode mode) { + int halfwidth = (width + 1) >> 1; + int halfheight = (height + 1) >> 1; + if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y || + !dst_u || !dst_v) { + return -1; + } + // Negative height means invert the image. + if (height < 0) { + height = -height; + src_y = src_y + (height - 1) * src_stride_y; + src_u = src_u + (height - 1) * src_stride_u; + src_v = src_v + (height - 1) * src_stride_v; + src_stride_y = -src_stride_y; + src_stride_u = -src_stride_u; + src_stride_v = -src_stride_v; + } + + switch (mode) { + case libyuv::kRotate0: + // copy frame + libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, + height); + libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, + height); + libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, + height); + return 0; + case libyuv::kRotate90: + // We need to rotate and rescale, we use plane Y as temporal storage. + libyuv::RotatePlane90(src_u, src_stride_u, dst_y, height, halfwidth, + height); + libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight, + halfheight, width, libyuv::kFilterBilinear); + libyuv::RotatePlane90(src_v, src_stride_v, dst_y, height, halfwidth, + height); + libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight, + halfheight, width, libyuv::kFilterLinear); + libyuv::RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, + height); + return 0; + case libyuv::kRotate270: + // We need to rotate and rescale, we use plane Y as temporal storage. + libyuv::RotatePlane270(src_u, src_stride_u, dst_y, height, halfwidth, + height); + libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight, + halfheight, width, libyuv::kFilterBilinear); + libyuv::RotatePlane270(src_v, src_stride_v, dst_y, height, halfwidth, + height); + libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight, + halfheight, width, libyuv::kFilterLinear); + libyuv::RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, + height); + + return 0; + case libyuv::kRotate180: + libyuv::RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, + height); + libyuv::RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, + halfwidth, height); + libyuv::RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, + halfwidth, height); + return 0; + default: + break; + } + return -1; +} + +// TODO(sergio.garcia.murillo@gmail.com): Remove this function with libyuv one +// as soon as the dependency is updated. +int webrtcI422Scale(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + int src_width, + int src_height, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int dst_width, + int dst_height, + enum libyuv::FilterMode filtering) { + if (!src_y || !src_u || !src_v || src_width <= 0 || src_height == 0 || + src_width > 32768 || src_height > 32768 || !dst_y || !dst_u || !dst_v || + dst_width <= 0 || dst_height <= 0) { + return -1; + } + int src_halfwidth = (src_width + 1) >> 1; + int dst_halfwidth = (dst_width + 1) >> 1; + + libyuv::ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, + dst_stride_y, dst_width, dst_height, filtering); + libyuv::ScalePlane(src_u, src_stride_u, src_halfwidth, src_height, dst_u, + dst_stride_u, dst_halfwidth, dst_height, filtering); + libyuv::ScalePlane(src_v, src_stride_v, src_halfwidth, src_height, dst_v, + dst_stride_v, dst_halfwidth, dst_height, filtering); + return 0; +} +} // namespace + +I422Buffer::I422Buffer(int width, int height) + : I422Buffer(width, height, width, (width + 1) / 2, (width + 1) / 2) {} + +I422Buffer::I422Buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) + : width_(width), + height_(height), + stride_y_(stride_y), + stride_u_(stride_u), + stride_v_(stride_v), + data_(static_cast( + AlignedMalloc(I422DataSize(height, stride_y, stride_u, stride_v), + kBufferAlignment))) { + RTC_DCHECK_GT(width, 0); + RTC_DCHECK_GT(height, 0); + RTC_DCHECK_GE(stride_y, width); + RTC_DCHECK_GE(stride_u, (width + 1) / 2); + RTC_DCHECK_GE(stride_v, (width + 1) / 2); +} + +I422Buffer::~I422Buffer() {} + +// static +rtc::scoped_refptr I422Buffer::Create(int width, int height) { + return rtc::make_ref_counted(width, height); +} + +// static +rtc::scoped_refptr I422Buffer::Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return rtc::make_ref_counted(width, height, stride_y, stride_u, + stride_v); +} + +// static +rtc::scoped_refptr I422Buffer::Copy( + const I422BufferInterface& source) { + return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), + source.DataU(), source.StrideU(), source.DataV(), + source.StrideV()); +} + +// static +rtc::scoped_refptr I422Buffer::Copy( + const I420BufferInterface& source) { + const int width = source.width(); + const int height = source.height(); + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ( + 0, libyuv::I420ToI422( + source.DataY(), source.StrideY(), source.DataU(), source.StrideU(), + source.DataV(), source.StrideV(), buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), width, height)); + return buffer; +} + +// static +rtc::scoped_refptr I422Buffer::Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v) { + // Note: May use different strides than the input data. + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ(0, libyuv::I422Copy(data_y, stride_y, data_u, stride_u, data_v, + stride_v, buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), + buffer->StrideU(), buffer->MutableDataV(), + buffer->StrideV(), width, height)); + return buffer; +} + +// static +rtc::scoped_refptr I422Buffer::Rotate( + const I422BufferInterface& src, + VideoRotation rotation) { + RTC_CHECK(src.DataY()); + RTC_CHECK(src.DataU()); + RTC_CHECK(src.DataV()); + + int rotated_width = src.width(); + int rotated_height = src.height(); + if (rotation == webrtc::kVideoRotation_90 || + rotation == webrtc::kVideoRotation_270) { + std::swap(rotated_width, rotated_height); + } + + rtc::scoped_refptr buffer = + I422Buffer::Create(rotated_width, rotated_height); + + RTC_CHECK_EQ(0, + webrtcI422Rotate( + src.DataY(), src.StrideY(), src.DataU(), src.StrideU(), + src.DataV(), src.StrideV(), buffer->MutableDataY(), + buffer->StrideY(), buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), src.width(), + src.height(), static_cast(rotation))); + + return buffer; +} + +rtc::scoped_refptr I422Buffer::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + libyuv::I422ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), + i420_buffer->MutableDataY(), i420_buffer->StrideY(), + i420_buffer->MutableDataU(), i420_buffer->StrideU(), + i420_buffer->MutableDataV(), i420_buffer->StrideV(), + width(), height()); + return i420_buffer; +} + +void I422Buffer::InitializeData() { + memset(data_.get(), 0, + I422DataSize(height_, stride_y_, stride_u_, stride_v_)); +} + +int I422Buffer::width() const { + return width_; +} + +int I422Buffer::height() const { + return height_; +} + +const uint8_t* I422Buffer::DataY() const { + return data_.get(); +} +const uint8_t* I422Buffer::DataU() const { + return data_.get() + stride_y_ * height_; +} +const uint8_t* I422Buffer::DataV() const { + return data_.get() + stride_y_ * height_ + stride_u_ * height_; +} + +int I422Buffer::StrideY() const { + return stride_y_; +} +int I422Buffer::StrideU() const { + return stride_u_; +} +int I422Buffer::StrideV() const { + return stride_v_; +} + +uint8_t* I422Buffer::MutableDataY() { + return const_cast(DataY()); +} +uint8_t* I422Buffer::MutableDataU() { + return const_cast(DataU()); +} +uint8_t* I422Buffer::MutableDataV() { + return const_cast(DataV()); +} + +void I422Buffer::CropAndScaleFrom(const I422BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height) { + RTC_CHECK_LE(crop_width, src.width()); + RTC_CHECK_LE(crop_height, src.height()); + RTC_CHECK_LE(crop_width + offset_x, src.width()); + RTC_CHECK_LE(crop_height + offset_y, src.height()); + RTC_CHECK_GE(offset_x, 0); + RTC_CHECK_GE(offset_y, 0); + + // Make sure offset is even so that u/v plane becomes aligned. + const int uv_offset_x = offset_x / 2; + const int uv_offset_y = offset_y; + offset_x = uv_offset_x * 2; + + const uint8_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; + const uint8_t* u_plane = + src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x; + const uint8_t* v_plane = + src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x; + int res = + webrtcI422Scale(y_plane, src.StrideY(), u_plane, src.StrideU(), v_plane, + src.StrideV(), crop_width, crop_height, MutableDataY(), + StrideY(), MutableDataU(), StrideU(), MutableDataV(), + StrideV(), width(), height(), libyuv::kFilterBox); + + RTC_DCHECK_EQ(res, 0); +} + +} // namespace webrtc diff --git a/api/video/i422_buffer.h b/api/video/i422_buffer.h new file mode 100644 index 0000000000..16c717469b --- /dev/null +++ b/api/video/i422_buffer.h @@ -0,0 +1,114 @@ +/* + * 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 API_VIDEO_I422_BUFFER_H_ +#define API_VIDEO_I422_BUFFER_H_ + +#include + +#include + +#include "api/scoped_refptr.h" +#include "api/video/video_frame_buffer.h" +#include "api/video/video_rotation.h" +#include "rtc_base/memory/aligned_malloc.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Plain I422 buffer in standard memory. +class RTC_EXPORT I422Buffer : public I422BufferInterface { + public: + static rtc::scoped_refptr Create(int width, int height); + static rtc::scoped_refptr Create(int width, + int height, + int stride_y, + int stride_u, + int stride_v); + + // Create a new buffer and copy the pixel data. + static rtc::scoped_refptr Copy(const I422BufferInterface& buffer); + /// Convert and put I420 buffer into a new buffer. + static rtc::scoped_refptr Copy(const I420BufferInterface& buffer); + + static rtc::scoped_refptr Copy(int width, + int height, + const uint8_t* data_y, + int stride_y, + const uint8_t* data_u, + int stride_u, + const uint8_t* data_v, + int stride_v); + + // Returns a rotated copy of `src`. + static rtc::scoped_refptr Rotate(const I422BufferInterface& src, + VideoRotation rotation); + + rtc::scoped_refptr ToI420() final; + const I420BufferInterface* GetI420() const final { return nullptr; } + + // Sets the buffer to all black. + static void SetBlack(I422Buffer* buffer); + + // Sets all three planes to all zeros. Used to work around for + // quirks in memory checkers + // (https://bugs.chromium.org/p/libyuv/issues/detail?id=377) and + // ffmpeg (http://crbug.com/390941). + // TODO(nisse): Deprecated. Should be deleted if/when those issues + // are resolved in a better way. Or in the mean time, use SetBlack. + void InitializeData(); + + int width() const override; + int height() const override; + const uint8_t* DataY() const override; + const uint8_t* DataU() const override; + const uint8_t* DataV() const override; + + int StrideY() const override; + int StrideU() const override; + int StrideV() const override; + + uint8_t* MutableDataY(); + uint8_t* MutableDataU(); + uint8_t* MutableDataV(); + + // Scale the cropped area of `src` to the size of `this` buffer, and + // write the result into `this`. + void CropAndScaleFrom(const I422BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height); + + // The common case of a center crop, when needed to adjust the + // aspect ratio without distorting the image. + void CropAndScaleFrom(const I422BufferInterface& src); + + // Scale all of `src` to the size of `this` buffer, with no cropping. + void ScaleFrom(const I422BufferInterface& src); + + protected: + I422Buffer(int width, int height); + I422Buffer(int width, int height, int stride_y, int stride_u, int stride_v); + + ~I422Buffer() override; + + private: + const int width_; + const int height_; + const int stride_y_; + const int stride_u_; + const int stride_v_; + const std::unique_ptr data_; +}; + +} // namespace webrtc + +#endif // API_VIDEO_I422_BUFFER_H_ diff --git a/api/video/test/BUILD.gn b/api/video/test/BUILD.gn index 5b0d57b3c6..5d654cff1a 100644 --- a/api/video/test/BUILD.gn +++ b/api/video/test/BUILD.gn @@ -12,6 +12,7 @@ rtc_library("rtc_api_video_unittests") { testonly = true sources = [ "color_space_unittest.cc", + "i422_buffer_unittest.cc", "i444_buffer_unittest.cc", "nv12_buffer_unittest.cc", "video_adaptation_counters_unittest.cc", @@ -38,3 +39,15 @@ rtc_source_set("mock_recordable_encoded_frame") { "../../../test:test_support", ] } + +rtc_source_set("video_frame_matchers") { + testonly = true + visibility = [ "*" ] + sources = [ "video_frame_matchers.h" ] + + deps = [ + "..:video_frame", + "../..:rtp_packet_info", + "../../../test:test_support", + ] +} diff --git a/api/video/test/i422_buffer_unittest.cc b/api/video/test/i422_buffer_unittest.cc new file mode 100644 index 0000000000..499b268546 --- /dev/null +++ b/api/video/test/i422_buffer_unittest.cc @@ -0,0 +1,128 @@ + +/* + * Copyright (c) 2022 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/video/i422_buffer.h" + +#include "api/video/i420_buffer.h" +#include "test/frame_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +int GetY(rtc::scoped_refptr buf, int col, int row) { + return buf->DataY()[row * buf->StrideY() + col]; +} + +int GetU(rtc::scoped_refptr buf, int col, int row) { + return buf->DataU()[row * buf->StrideU() + col]; +} + +int GetV(rtc::scoped_refptr buf, int col, int row) { + return buf->DataV()[row * buf->StrideV() + col]; +} + +void FillI422Buffer(rtc::scoped_refptr buf) { + const uint8_t Y = 1; + const uint8_t U = 2; + const uint8_t V = 3; + for (int row = 0; row < buf->height(); ++row) { + for (int col = 0; col < buf->width(); ++col) { + buf->MutableDataY()[row * buf->StrideY() + col] = Y; + } + } + for (int row = 0; row < buf->ChromaHeight(); ++row) { + for (int col = 0; col < buf->ChromaWidth(); ++col) { + buf->MutableDataU()[row * buf->StrideU() + col] = U; + buf->MutableDataV()[row * buf->StrideV() + col] = V; + } + } +} + +} // namespace + +TEST(I422BufferTest, InitialData) { + constexpr int stride = 3; + constexpr int halfstride = (stride + 1) >> 1; + constexpr int width = 3; + constexpr int halfwidth = (width + 1) >> 1; + constexpr int height = 3; + + rtc::scoped_refptr i422_buffer(I422Buffer::Create(width, height)); + EXPECT_EQ(width, i422_buffer->width()); + EXPECT_EQ(height, i422_buffer->height()); + EXPECT_EQ(stride, i422_buffer->StrideY()); + EXPECT_EQ(halfstride, i422_buffer->StrideU()); + EXPECT_EQ(halfstride, i422_buffer->StrideV()); + EXPECT_EQ(halfwidth, i422_buffer->ChromaWidth()); + EXPECT_EQ(height, i422_buffer->ChromaHeight()); +} + +TEST(I422BufferTest, ReadPixels) { + constexpr int width = 3; + constexpr int halfwidth = (width + 1) >> 1; + constexpr int height = 3; + + rtc::scoped_refptr i422_buffer(I422Buffer::Create(width, height)); + // Y = 1, U = 2, V = 3. + FillI422Buffer(i422_buffer); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(1, GetY(i422_buffer, col, row)); + } + } + for (int row = 0; row < height; row++) { + for (int col = 0; col < halfwidth; col++) { + EXPECT_EQ(2, GetU(i422_buffer, col, row)); + EXPECT_EQ(3, GetV(i422_buffer, col, row)); + } + } +} + +TEST(I422BufferTest, ToI420) { + constexpr int width = 3; + constexpr int halfwidth = (width + 1) >> 1; + constexpr int height = 3; + constexpr int size = width * height; + constexpr int halfsize = (width + 1) / 2 * height; + constexpr int quartersize = (width + 1) / 2 * (height + 1) / 2; + rtc::scoped_refptr reference(I420Buffer::Create(width, height)); + memset(reference->MutableDataY(), 8, size); + memset(reference->MutableDataU(), 4, quartersize); + memset(reference->MutableDataV(), 2, quartersize); + + rtc::scoped_refptr i422_buffer(I422Buffer::Create(width, height)); + // Convert the reference buffer to I422. + memset(i422_buffer->MutableDataY(), 8, size); + memset(i422_buffer->MutableDataU(), 4, halfsize); + memset(i422_buffer->MutableDataV(), 2, halfsize); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(8, GetY(i422_buffer, col, row)); + } + } + for (int row = 0; row < height; row++) { + for (int col = 0; col < halfwidth; col++) { + EXPECT_EQ(4, GetU(i422_buffer, col, row)); + EXPECT_EQ(2, GetV(i422_buffer, col, row)); + } + } + + rtc::scoped_refptr i420_buffer(i422_buffer->ToI420()); + EXPECT_EQ(height, i420_buffer->height()); + EXPECT_EQ(width, i420_buffer->width()); + EXPECT_TRUE(test::FrameBufsEqual(reference, i420_buffer)); +} + +} // namespace webrtc diff --git a/api/video/test/video_frame_matchers.h b/api/video/test/video_frame_matchers.h new file mode 100644 index 0000000000..250459377b --- /dev/null +++ b/api/video/test/video_frame_matchers.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 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_VIDEO_TEST_VIDEO_FRAME_MATCHERS_H_ +#define API_VIDEO_TEST_VIDEO_FRAME_MATCHERS_H_ + +#include "api/rtp_packet_infos.h" +#include "api/video/video_frame.h" +#include "test/gmock.h" + +namespace webrtc::test::video_frame_matchers { + +MATCHER_P(Rotation, rotation, "") { + return ::testing::Matches(::testing::Eq(rotation))(arg.rotation()); +} + +MATCHER_P(NtpTimestamp, ntp_ts, "") { + return arg.ntp_time_ms() == ntp_ts.ms(); +} + +MATCHER_P(PacketInfos, m, "") { + return ::testing::Matches(m)(arg.packet_infos()); +} + +} // namespace webrtc::test::video_frame_matchers + +#endif // API_VIDEO_TEST_VIDEO_FRAME_MATCHERS_H_ diff --git a/api/video/video_frame_buffer.cc b/api/video/video_frame_buffer.cc index 6c46f782a0..25bca9ab19 100644 --- a/api/video/video_frame_buffer.cc +++ b/api/video/video_frame_buffer.cc @@ -11,6 +11,7 @@ #include "api/video/video_frame_buffer.h" #include "api/video/i420_buffer.h" +#include "api/video/i422_buffer.h" #include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/checks.h" @@ -47,6 +48,11 @@ const I444BufferInterface* VideoFrameBuffer::GetI444() const { return static_cast(this); } +const I422BufferInterface* VideoFrameBuffer::GetI422() const { + RTC_CHECK(type() == Type::kI422); + return static_cast(this); +} + const I010BufferInterface* VideoFrameBuffer::GetI010() const { RTC_CHECK(type() == Type::kI010); return static_cast(this); @@ -77,6 +83,8 @@ const char* VideoFrameBufferTypeToString(VideoFrameBuffer::Type type) { return "kI420A"; case VideoFrameBuffer::Type::kI444: return "kI444"; + case VideoFrameBuffer::Type::kI422: + return "kI422"; case VideoFrameBuffer::Type::kI010: return "kI010"; case VideoFrameBuffer::Type::kNV12: @@ -131,6 +139,31 @@ rtc::scoped_refptr I444BufferInterface::CropAndScale( return result; } +VideoFrameBuffer::Type I422BufferInterface::type() const { + return Type::kI422; +} + +int I422BufferInterface::ChromaWidth() const { + return (width() + 1) / 2; +} + +int I422BufferInterface::ChromaHeight() const { + return height(); +} + +rtc::scoped_refptr I422BufferInterface::CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + rtc::scoped_refptr result = + I422Buffer::Create(scaled_width, scaled_height); + result->CropAndScaleFrom(*this, offset_x, offset_y, crop_width, crop_height); + return result; +} + VideoFrameBuffer::Type I010BufferInterface::type() const { return Type::kI010; } diff --git a/api/video/video_frame_buffer.h b/api/video/video_frame_buffer.h index 6098a48117..23a40bebfc 100644 --- a/api/video/video_frame_buffer.h +++ b/api/video/video_frame_buffer.h @@ -22,6 +22,7 @@ namespace webrtc { class I420BufferInterface; class I420ABufferInterface; +class I422BufferInterface; class I444BufferInterface; class I010BufferInterface; class NV12BufferInterface; @@ -52,6 +53,7 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface { kNative, kI420, kI420A, + kI422, kI444, kI010, kNV12, @@ -104,6 +106,7 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface { // These functions should only be called if type() is of the correct type. // Calling with a different type will result in a crash. const I420ABufferInterface* GetI420A() const; + const I422BufferInterface* GetI422() const; const I444BufferInterface* GetI444() const; const I010BufferInterface* GetI010() const; const NV12BufferInterface* GetNV12() const; @@ -140,7 +143,7 @@ class PlanarYuvBuffer : public VideoFrameBuffer { }; // This interface represents 8-bit color depth formats: Type::kI420, -// Type::kI420A and Type::kI444. +// Type::kI420A, Type::kI422 and Type::kI444. class PlanarYuv8Buffer : public PlanarYuvBuffer { public: // Returns pointer to the pixel data for a given plane. The memory is owned by @@ -177,6 +180,26 @@ class RTC_EXPORT I420ABufferInterface : public I420BufferInterface { ~I420ABufferInterface() override {} }; +// Represents Type::kI422, 4:2:2 planar with 8 bits per pixel. +class I422BufferInterface : public PlanarYuv8Buffer { + public: + Type type() const final; + + int ChromaWidth() const final; + int ChromaHeight() const final; + + rtc::scoped_refptr CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + + protected: + ~I422BufferInterface() override {} +}; + +// Represents Type::kI444, 4:4:4 planar with 8 bits per pixel. class I444BufferInterface : public PlanarYuv8Buffer { public: Type type() const final; diff --git a/api/video/video_stream_decoder_create.cc b/api/video/video_stream_decoder_create.cc index 8d70556b4d..e14c3bc851 100644 --- a/api/video/video_stream_decoder_create.cc +++ b/api/video/video_stream_decoder_create.cc @@ -20,10 +20,13 @@ std::unique_ptr CreateVideoStreamDecoder( VideoStreamDecoderInterface::Callbacks* callbacks, VideoDecoderFactory* decoder_factory, TaskQueueFactory* task_queue_factory, - std::map> decoder_settings) { - return std::make_unique(callbacks, decoder_factory, - task_queue_factory, - std::move(decoder_settings)); + std::map> decoder_settings, + // TODO(jonaso, webrtc:10335): Consider what to do with factories + // vs. field trials. + const FieldTrialsView* field_trials) { + return std::make_unique( + callbacks, decoder_factory, task_queue_factory, + std::move(decoder_settings), field_trials); } } // namespace webrtc diff --git a/api/video/video_stream_decoder_create.h b/api/video/video_stream_decoder_create.h index 9c898ec610..974fd804ce 100644 --- a/api/video/video_stream_decoder_create.h +++ b/api/video/video_stream_decoder_create.h @@ -15,6 +15,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "api/video/video_stream_decoder.h" #include "api/video_codecs/sdp_video_format.h" @@ -28,7 +29,8 @@ std::unique_ptr CreateVideoStreamDecoder( VideoStreamDecoderInterface::Callbacks* callbacks, VideoDecoderFactory* decoder_factory, TaskQueueFactory* task_queue_factory, - std::map> decoder_settings); + std::map> decoder_settings, + const FieldTrialsView* field_trials = nullptr); } // namespace webrtc diff --git a/api/video/video_timing.cc b/api/video/video_timing.cc index df1bc4857a..0483c20e66 100644 --- a/api/video/video_timing.cc +++ b/api/video/video_timing.cc @@ -11,6 +11,7 @@ #include "api/video/video_timing.h" #include "api/array_view.h" +#include "api/units/time_delta.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/strings/string_builder.h" @@ -25,6 +26,14 @@ uint16_t VideoSendTiming::GetDeltaCappedMs(int64_t base_ms, int64_t time_ms) { return rtc::saturated_cast(time_ms - base_ms); } +uint16_t VideoSendTiming::GetDeltaCappedMs(TimeDelta delta) { + if (delta < TimeDelta::Zero()) { + RTC_DLOG(LS_ERROR) << "Delta " << delta.ms() + << "ms expected to be positive"; + } + return rtc::saturated_cast(delta.ms()); +} + TimingFrameInfo::TimingFrameInfo() : rtp_timestamp(0), capture_time_ms(-1), diff --git a/api/video/video_timing.h b/api/video/video_timing.h index dd8febb3db..698477a81a 100644 --- a/api/video/video_timing.h +++ b/api/video/video_timing.h @@ -16,6 +16,8 @@ #include #include +#include "api/units/time_delta.h" + namespace webrtc { // Video timing timestamps in ms counted from capture_time_ms of a frame. @@ -34,6 +36,7 @@ struct VideoSendTiming { // https://webrtc.org/experiments/rtp-hdrext/video-timing/ extension stores // 16-bit deltas of timestamps from packet capture time. static uint16_t GetDeltaCappedMs(int64_t base_ms, int64_t time_ms); + static uint16_t GetDeltaCappedMs(TimeDelta delta); uint16_t encode_start_delta_ms; uint16_t encode_finish_delta_ms; diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn index cab0f7a049..8ca9f7afcd 100644 --- a/api/video_codecs/BUILD.gn +++ b/api/video_codecs/BUILD.gn @@ -12,6 +12,10 @@ if (is_android) { import("//build/config/android/rules.gni") } +rtc_source_set("scalability_mode") { + sources = [ "scalability_mode.h" ] +} + rtc_library("video_codecs_api") { visibility = [ "*" ] sources = [ @@ -41,12 +45,15 @@ rtc_library("video_codecs_api") { ] deps = [ + ":scalability_mode", "..:fec_controller_api", "..:scoped_refptr", "../../api:array_view", "../../modules/video_coding:codec_globals_headers", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:refcount", + "../../rtc_base:stringutils", "../../rtc_base/system:rtc_export", "../units:data_rate", "../video:encoded_image", @@ -112,6 +119,58 @@ rtc_library("builtin_video_encoder_factory") { absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } +rtc_source_set("video_encoder_factory_template") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template.h" ] + + deps = [ + ":video_codecs_api", + "../../modules/video_coding/svc:scalability_mode_util", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ] +} + +rtc_source_set("video_encoder_factory_template_libvpx_vp8_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libvpx_vp8_adapter.h" ] + + deps = [ + "../../modules/video_coding:webrtc_vp8", + "../../modules/video_coding:webrtc_vp8_scalability", + ] +} + +rtc_source_set("video_encoder_factory_template_libvpx_vp9_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libvpx_vp9_adapter.h" ] + + deps = [ "../../modules/video_coding:webrtc_vp9" ] +} + +rtc_source_set("video_encoder_factory_template_open_h264_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_open_h264_adapter.h" ] + + deps = [ "../../modules/video_coding:webrtc_h264" ] +} + +rtc_source_set("video_encoder_factory_template_libaom_av1_adapter") { + visibility = [ "*" ] + allow_poison = [ "software_video_codecs" ] + public = [ "video_encoder_factory_template_libaom_av1_adapter.h" ] + + deps = [ + ":scalability_mode", + "../../modules/video_coding/codecs/av1:av1_svc_config", + "../../modules/video_coding/codecs/av1:libaom_av1_encoder", + "../../modules/video_coding/svc:scalability_mode_util", + ] +} + rtc_library("vp8_temporal_layers_factory") { visibility = [ "*" ] allow_poison = [ "software_video_codecs" ] @@ -147,7 +206,8 @@ rtc_library("rtc_software_fallback_wrappers") { "../../modules/video_coding:video_codec_interface", "../../modules/video_coding:video_coding_utility", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", "../../rtc_base/system:rtc_export", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", diff --git a/api/video_codecs/scalability_mode.h b/api/video_codecs/scalability_mode.h new file mode 100644 index 0000000000..262e7ced7b --- /dev/null +++ b/api/video_codecs/scalability_mode.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_SCALABILITY_MODE_H_ +#define API_VIDEO_CODECS_SCALABILITY_MODE_H_ + +namespace webrtc { + +// Supported scalability modes. Most applications should use the +// PeerConnection-level apis where scalability mode is represented as a string. +// This list of currently recognized modes is intended for the api boundary +// between webrtc and injected encoders. Any application usage outside of +// injected encoders is strongly discouraged. +enum class ScalabilityMode { + kL1T1, + kL1T2, + kL1T2h, + kL1T3, + kL1T3h, + kL2T1, + kL2T1h, + kL2T1_KEY, + kL2T2, + kL2T2h, + kL2T2_KEY, + kL2T2_KEY_SHIFT, + kL2T3, + kL2T3h, + kL2T3_KEY, + kL3T1, + kL3T1h, + kL3T1_KEY, + kL3T2, + kL3T2h, + kL3T2_KEY, + kL3T3, + kL3T3h, + kL3T3_KEY, + kS2T1, + kS3T3, +}; + +} // namespace webrtc +#endif // API_VIDEO_CODECS_SCALABILITY_MODE_H_ diff --git a/api/video_codecs/test/BUILD.gn b/api/video_codecs/test/BUILD.gn index 14b54a1f99..3bee6b17ce 100644 --- a/api/video_codecs/test/BUILD.gn +++ b/api/video_codecs/test/BUILD.gn @@ -20,6 +20,7 @@ if (rtc_include_tests) { ] deps = [ + ":video_encoder_factory_template_tests", "..:builtin_video_encoder_factory", "..:rtc_software_fallback_wrappers", "..:video_codecs_api", @@ -43,4 +44,20 @@ if (rtc_include_tests) { ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } + + rtc_library("video_encoder_factory_template_tests") { + testonly = true + sources = [ "video_encoder_factory_template_tests.cc" ] + + deps = [ + "..:video_encoder_factory_template", + "..:video_encoder_factory_template_libaom_av1_adapter", + "..:video_encoder_factory_template_libvpx_vp8_adapter", + "..:video_encoder_factory_template_libvpx_vp9_adapter", + "..:video_encoder_factory_template_open_h264_adapter", + "../../:mock_video_encoder", + "../../../test:test_support", + "//testing/gtest", + ] + } } diff --git a/api/video_codecs/test/video_encoder_factory_template_tests.cc b/api/video_codecs/test/video_encoder_factory_template_tests.cc new file mode 100644 index 0000000000..b9dfac9d9e --- /dev/null +++ b/api/video_codecs/test/video_encoder_factory_template_tests.cc @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022 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/test/mock_video_encoder.h" +#include "api/video_codecs/video_encoder_factory_template.h" +#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" +#include "test/gmock.h" +#include "test/gtest.h" + +using ::testing::Each; +using ::testing::Eq; +using ::testing::Field; +using ::testing::IsEmpty; +using ::testing::Ne; +using ::testing::Not; +using ::testing::UnorderedElementsAre; + +namespace webrtc { +namespace { +using CodecSupport = VideoEncoderFactory::CodecSupport; +const SdpVideoFormat kFooSdp("Foo"); +const SdpVideoFormat kBarLowSdp("Bar", {{"profile", "low"}}); +const SdpVideoFormat kBarHighSdp("Bar", {{"profile", "high"}}); + +struct FooEncoderTemplateAdapter { + static std::vector SupportedFormats() { return {kFooSdp}; } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return scalability_mode == ScalabilityMode::kL1T2 || + scalability_mode == ScalabilityMode::kL1T3; + } +}; + +struct BarEncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {kBarLowSdp, kBarHighSdp}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return std::make_unique>(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return scalability_mode == ScalabilityMode::kL1T2 || + scalability_mode == ScalabilityMode::kL1T3 || + scalability_mode == ScalabilityMode::kS2T1 || + scalability_mode == ScalabilityMode::kS3T3; + } +}; + +TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCreateEncoder) { + VideoEncoderFactoryTemplate factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), Eq(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, OneTemplateAdapterCodecSupport) { + VideoEncoderFactoryTemplate factory; + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S3T3"), + Field(&CodecSupport::is_supported, false)); + EXPECT_THAT(factory.QueryCodecSupport(SdpVideoFormat("FooX"), absl::nullopt), + Field(&CodecSupport::is_supported, false)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersNoDuplicates) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kFooSdp)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCreateEncoders) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.GetSupportedFormats(), + UnorderedElementsAre(kFooSdp, kBarLowSdp, kBarHighSdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kFooSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(kBarLowSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(kBarHighSdp), Ne(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("FooX")), Eq(nullptr)); + EXPECT_THAT(factory.CreateVideoEncoder(SdpVideoFormat("Bar")), Eq(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, TwoTemplateAdaptersCodecSupport) { + VideoEncoderFactoryTemplate + factory; + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "L1T2"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kFooSdp, "S3T3"), + Field(&CodecSupport::is_supported, false)); + EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, absl::nullopt), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarLowSdp, "S2T1"), + Field(&CodecSupport::is_supported, true)); + EXPECT_THAT(factory.QueryCodecSupport(kBarHighSdp, "S3T2"), + Field(&CodecSupport::is_supported, false)); +} + +TEST(VideoEncoderFactoryTemplate, LibvpxVp8) { + VideoEncoderFactoryTemplate factory; + const SdpVideoFormat kVp8Sdp("VP8"); + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kVp8Sdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kVp8Sdp), Ne(nullptr)); +} + +TEST(VideoEncoderFactoryTemplate, LibvpxVp9) { + VideoEncoderFactoryTemplate factory; + auto formats = factory.GetSupportedFormats(); + EXPECT_THAT(formats, Not(IsEmpty())); + EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "VP9"))); + EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), Ne(nullptr)); +} + +// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build +// target remove this #ifdef. +#if defined(WEBRTC_USE_H264) +TEST(VideoEncoderFactoryTemplate, OpenH264) { + VideoEncoderFactoryTemplate factory; + auto formats = factory.GetSupportedFormats(); + EXPECT_THAT(formats, Not(IsEmpty())); + EXPECT_THAT(formats, Each(Field(&SdpVideoFormat::name, "H264"))); + EXPECT_THAT(factory.CreateVideoEncoder(formats[0]), Ne(nullptr)); +} +#endif // defined(WEBRTC_USE_H264) + +TEST(VideoEncoderFactoryTemplate, LibaomAv1) { + VideoEncoderFactoryTemplate factory; + const SdpVideoFormat kAv1Sdp("AV1"); + EXPECT_THAT(factory.GetSupportedFormats(), UnorderedElementsAre(kAv1Sdp)); + EXPECT_THAT(factory.CreateVideoEncoder(kAv1Sdp), Ne(nullptr)); +} + +} // namespace +} // namespace webrtc diff --git a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc index 2150a767a3..0e64a1031b 100644 --- a/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc +++ b/api/video_codecs/test/video_encoder_software_fallback_wrapper_unittest.cc @@ -203,7 +203,7 @@ void VideoEncoderSoftwareFallbackWrapperTestBase::EncodeFrame( int expected_ret) { rtc::scoped_refptr buffer = I420Buffer::Create(codec_.width, codec_.height); - I420Buffer::SetBlack(buffer); + I420Buffer::SetBlack(buffer.get()); std::vector types(1, VideoFrameType::kVideoFrameKey); frame_ = diff --git a/api/video_codecs/video_codec.cc b/api/video_codecs/video_codec.cc index d850917ac2..75c89e6985 100644 --- a/api/video_codecs/video_codec.cc +++ b/api/video_codecs/video_codec.cc @@ -139,4 +139,23 @@ VideoCodecType PayloadStringToCodecType(const std::string& name) { return kVideoCodecGeneric; } +VideoCodecComplexity VideoCodec::GetVideoEncoderComplexity() const { + if (complexity_.has_value()) { + return complexity_.value(); + } + switch (codecType) { + case kVideoCodecVP8: + return VP8().complexity; + case kVideoCodecVP9: + return VP9().complexity; + default: + return VideoCodecComplexity::kComplexityNormal; + } +} + +void VideoCodec::SetVideoEncoderComplexity( + VideoCodecComplexity complexity_setting) { + complexity_ = complexity_setting; +} + } // namespace webrtc diff --git a/api/video_codecs/video_codec.h b/api/video_codecs/video_codec.h index 1e6e21c0ec..9210d2563d 100644 --- a/api/video_codecs/video_codec.h +++ b/api/video_codecs/video_codec.h @@ -19,6 +19,7 @@ #include "absl/strings/string_view.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_codec_type.h" +#include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/spatial_layer.h" #include "rtc_base/system/rtc_export.h" @@ -29,6 +30,7 @@ namespace webrtc { // Video codec enum class VideoCodecComplexity { + kComplexityLow = -1, kComplexityNormal = 0, kComplexityHigh = 1, kComplexityHigher = 2, @@ -103,11 +105,16 @@ class RTC_EXPORT VideoCodec { // Scalability mode as described in // https://www.w3.org/TR/webrtc-svc/#scalabilitymodes* - // or value 'NONE' to indicate no scalability. - absl::string_view ScalabilityMode() const { return scalability_mode_; } - void SetScalabilityMode(absl::string_view scalability_mode) { - scalability_mode_ = std::string(scalability_mode); + absl::optional GetScalabilityMode() const { + return scalability_mode_; } + void SetScalabilityMode(ScalabilityMode scalability_mode) { + scalability_mode_ = scalability_mode; + } + void UnsetScalabilityMode() { scalability_mode_ = absl::nullopt; } + + VideoCodecComplexity GetVideoEncoderComplexity() const; + void SetVideoEncoderComplexity(VideoCodecComplexity complexity_setting); // Public variables. TODO(hta): Make them private with accessors. VideoCodecType codecType; @@ -168,7 +175,10 @@ class RTC_EXPORT VideoCodec { // TODO(hta): Consider replacing the union with a pointer type. // This will allow removing the VideoCodec* types from this file. VideoCodecUnion codec_specific_; - std::string scalability_mode_; + absl::optional scalability_mode_; + // 'complexity_' indicates the CPU capability of the client. It's used to + // determine encoder CPU complexity (e.g., cpu_used for VP8, VP9. and AV1). + absl::optional complexity_; }; } // namespace webrtc diff --git a/api/video_codecs/video_decoder_software_fallback_wrapper.cc b/api/video_codecs/video_decoder_software_fallback_wrapper.cc index 6484616b81..cf6f823b92 100644 --- a/api/video_codecs/video_decoder_software_fallback_wrapper.cc +++ b/api/video_codecs/video_decoder_software_fallback_wrapper.cc @@ -83,8 +83,9 @@ VideoDecoderSoftwareFallbackWrapper::VideoDecoderSoftwareFallbackWrapper( hw_decoder_(std::move(hw_decoder)), fallback_decoder_(std::move(sw_fallback_decoder)), fallback_implementation_name_( - std::string(fallback_decoder_->ImplementationName()) + - " (fallback from: " + hw_decoder_->ImplementationName() + ")"), + fallback_decoder_->GetDecoderInfo().implementation_name + + " (fallback from: " + + hw_decoder_->GetDecoderInfo().implementation_name + ")"), callback_(nullptr), hw_decoded_frames_since_last_fallback_(0), hw_consequtive_generic_errors_(0) {} diff --git a/api/video_codecs/video_encoder.cc b/api/video_codecs/video_encoder.cc index 83c329152e..6d61e5dcad 100644 --- a/api/video_codecs/video_encoder.cc +++ b/api/video_codecs/video_encoder.cc @@ -45,6 +45,7 @@ VideoCodecVP9 VideoEncoder::GetDefaultVp9Settings() { vp9_settings.numberOfSpatialLayers = 1; vp9_settings.flexibleMode = false; vp9_settings.interLayerPred = InterLayerPredMode::kOn; + vp9_settings.complexity = VideoCodecComplexity::kComplexityNormal; return vp9_settings; } diff --git a/api/video_codecs/video_encoder_config.cc b/api/video_codecs/video_encoder_config.cc index 206a1d3984..92568d7bd1 100644 --- a/api/video_codecs/video_encoder_config.cc +++ b/api/video_codecs/video_encoder_config.cc @@ -79,7 +79,7 @@ std::string VideoEncoderConfig::ToString() const { break; } ss << ", encoder_specific_settings: "; - ss << (encoder_specific_settings != NULL ? "(ptr)" : "NULL"); + ss << (encoder_specific_settings != nullptr ? "(ptr)" : "NULL"); ss << ", min_transmit_bitrate_bps: " << min_transmit_bitrate_bps; ss << '}'; diff --git a/api/video_codecs/video_encoder_config.h b/api/video_codecs/video_encoder_config.h index cfda2ad7cf..ace71af51b 100644 --- a/api/video_codecs/video_encoder_config.h +++ b/api/video_codecs/video_encoder_config.h @@ -18,6 +18,7 @@ #include "absl/types/optional.h" #include "api/scoped_refptr.h" +#include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_codec.h" #include "rtc_base/ref_count.h" @@ -64,7 +65,7 @@ struct VideoStream { // between multiple streams. absl::optional bitrate_priority; - absl::optional scalability_mode; + absl::optional scalability_mode; // If this stream is enabled by the user, or not. bool active; diff --git a/api/video_codecs/video_encoder_factory.h b/api/video_codecs/video_encoder_factory.h index d7cea47909..2914a41518 100644 --- a/api/video_codecs/video_encoder_factory.h +++ b/api/video_codecs/video_encoder_factory.h @@ -17,6 +17,7 @@ #include "absl/types/optional.h" #include "api/units/data_rate.h" +#include "api/video/render_resolution.h" #include "api/video_codecs/sdp_video_format.h" namespace webrtc { @@ -47,6 +48,13 @@ class VideoEncoderFactory { virtual absl::optional OnAvailableBitrate( const DataRate& rate) = 0; + // Called every time the encoder input resolution change. Should return a + // non-empty if an encoder switch should be performed. + virtual absl::optional OnResolutionChange( + const RenderResolution& resolution) { + return absl::nullopt; + } + // Called if the currently used encoder reports itself as broken. Should // return a non-empty if an encoder switch should be performed. virtual absl::optional OnEncoderBroken() = 0; diff --git a/api/video_codecs/video_encoder_factory_template.h b/api/video_codecs/video_encoder_factory_template.h new file mode 100644 index 0000000000..4913039d04 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ + +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "modules/video_coding/svc/scalability_mode_util.h" + +namespace webrtc { +// The VideoEncoderFactoryTemplate supports encoders implementations given as +// template arguments. +// +// To include an encoder in the factory it requires three static members +// functions to be defined: +// +// // Returns the supported SdpVideoFormats this encoder can produce. +// static std::vector SupportedFormats(); +// +// // Creates an encoder instance for the given format. +// static std::unique_ptr +// CreateEncoder(const SdpVideoFormat& format); +// +// // Returns true if the encoder supports the given scalability mode. +// static bool +// IsScalabilityModeSupported(ScalabilityMode scalability_mode); +// +// Note that the order of the template arguments matter as the factory will +// query/return the first encoder implementation supporting the given +// SdpVideoFormat. +template +class VideoEncoderFactoryTemplate : public VideoEncoderFactory { + public: + std::vector GetSupportedFormats() const override { + std::vector formats; + GetSupportedFormatsInternal(formats); + return formats; + } + + std::unique_ptr CreateVideoEncoder( + const SdpVideoFormat& format) override { + return CreateVideoEncoderInternal(format); + } + + CodecSupport QueryCodecSupport( + const SdpVideoFormat& format, + absl::optional scalability_mode) const override { + return QueryCodecSupportInternal(format, scalability_mode); + } + + private: + template + bool IsFormatSupported(const SdpVideoFormat& format) const { + return absl::c_count(V::SupportedFormats(), format) > 0; + } + + template + bool IsScalabilityModeSupported( + const absl::optional& scalability_mode_string) const { + if (!scalability_mode_string.has_value()) { + return true; + } + absl::optional scalability_mode = + ScalabilityModeFromString(*scalability_mode_string); + return scalability_mode.has_value() && + V::IsScalabilityModeSupported(*scalability_mode); + } + + template + void GetSupportedFormatsInternal(std::vector& formats) const { + auto supported_formats = V::SupportedFormats(); + for (const auto& format : supported_formats) { + if (absl::c_count(formats, format) == 0) { + formats.push_back(format); + } + } + + if constexpr (sizeof...(Vs) > 0) { + return GetSupportedFormatsInternal(formats); + } + } + + template + std::unique_ptr CreateVideoEncoderInternal( + const SdpVideoFormat& format) { + if (IsFormatSupported(format)) { + return V::CreateEncoder(format); + } + + if constexpr (sizeof...(Vs) > 0) { + return CreateVideoEncoderInternal(format); + } + + return nullptr; + } + + template + CodecSupport QueryCodecSupportInternal( + const SdpVideoFormat& format, + const absl::optional& scalability_mode) const { + if (IsFormatSupported(format)) { + return {.is_supported = IsScalabilityModeSupported(scalability_mode)}; + } + + if constexpr (sizeof...(Vs) > 0) { + return QueryCodecSupportInternal(format, scalability_mode); + } + + return {.is_supported = false}; + } +}; + +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h b/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h new file mode 100644 index 0000000000..d71cc00253 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/av1/av1_svc_config.h" +#include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" + +namespace webrtc { +struct LibaomAv1EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {SdpVideoFormat("AV1")}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return CreateLibaomAv1Encoder(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return LibaomAv1EncoderSupportsScalabilityMode(scalability_mode); + } +}; + +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBAOM_AV1_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h b/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h new file mode 100644 index 0000000000..c00c6d1250 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/vp8/include/vp8.h" +#include "modules/video_coding/codecs/vp8/vp8_scalability.h" + +namespace webrtc { +struct LibvpxVp8EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return {SdpVideoFormat("VP8")}; + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return VP8Encoder::Create(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return VP8SupportsScalabilityMode(scalability_mode); + } +}; +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP8_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h b/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h new file mode 100644 index 0000000000..c317cda16c --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/vp9/include/vp9.h" + +namespace webrtc { +struct LibvpxVp9EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return SupportedVP9Codecs(); + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return VP9Encoder::Create(); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return VP9Encoder::SupportsScalabilityMode(scalability_mode); + } +}; +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_LIBVPX_VP9_ADAPTER_H_ diff --git a/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h b/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h new file mode 100644 index 0000000000..9959617c62 --- /dev/null +++ b/api/video_codecs/video_encoder_factory_template_open_h264_adapter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ +#define API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ + +#include +#include + +#include "modules/video_coding/codecs/h264/include/h264.h" + +namespace webrtc { +// TODO(bugs.webrtc.org/13573): When OpenH264 is no longer a conditional build +// target remove this #ifdef. +#if defined(WEBRTC_USE_H264) +struct OpenH264EncoderTemplateAdapter { + static std::vector SupportedFormats() { + return SupportedH264Codecs(); + } + + static std::unique_ptr CreateEncoder( + const SdpVideoFormat& format) { + return H264Encoder::Create(cricket::VideoCodec(format)); + } + + static bool IsScalabilityModeSupported(ScalabilityMode scalability_mode) { + return H264Encoder::SupportsScalabilityMode(scalability_mode); + } +}; +#endif // defined(WEBRTC_USE_H264) +} // namespace webrtc + +#endif // API_VIDEO_CODECS_VIDEO_ENCODER_FACTORY_TEMPLATE_OPEN_H264_ADAPTER_H_ diff --git a/api/webrtc_key_value_config.h b/api/webrtc_key_value_config.h new file mode 100644 index 0000000000..e3cac59698 --- /dev/null +++ b/api/webrtc_key_value_config.h @@ -0,0 +1,17 @@ +/* + * 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_WEBRTC_KEY_VALUE_CONFIG_H_ +#define API_WEBRTC_KEY_VALUE_CONFIG_H_ + +// TODO(bugs.webrtc.org/10335): Remove once all migrated to +// api/field_trials_view.h +#include "api/field_trials_view.h" + +#endif // API_WEBRTC_KEY_VALUE_CONFIG_H_ diff --git a/audio/BUILD.gn b/audio/BUILD.gn index 23976ab45f..1878154b14 100644 --- a/audio/BUILD.gn +++ b/audio/BUILD.gn @@ -42,6 +42,7 @@ rtc_library("audio") { deps = [ "../api:array_view", "../api:call_api", + "../api:field_trials_view", "../api:frame_transformer_interface", "../api:function_view", "../api:rtp_headers", @@ -85,12 +86,21 @@ rtc_library("audio") { "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base", "../rtc_base:audio_format_to_string", + "../rtc_base:buffer", "../rtc_base:checks", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:race_checker", "../rtc_base:rate_limiter", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", + "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", + "../rtc_base:safe_conversions", "../rtc_base:safe_minmax", + "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/containers:flat_set", "../rtc_base/experiments:field_trial_parser", "../rtc_base/synchronization:mutex", @@ -104,6 +114,7 @@ rtc_library("audio") { ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } @@ -177,7 +188,8 @@ if (rtc_include_tests) { "../modules/rtp_rtcp:rtp_rtcp_format", "../modules/utility", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", "../rtc_base:safe_compare", "../rtc_base:task_queue_for_test", @@ -189,6 +201,7 @@ if (rtc_include_tests) { "../test:mock_transformable_frame", "../test:mock_transport", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_common", "../test:test_support", "utility:utility_tests", @@ -225,9 +238,17 @@ if (rtc_include_tests) { "../test/pc/e2e:network_quality_metrics_reporter", "//testing/gtest", ] - absl_deps = [ "//third_party/abseil-cpp/absl/flags:flag" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/strings", + ] if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] } data = [ "../resources/voice_engine/audio_tiny16.wav", @@ -278,8 +299,6 @@ if (rtc_include_tests) { if (is_mac) { data += [ "../tools_webrtc/audio_quality/mac/pesq" ] } - - write_runtime_deps = "${root_out_dir}/${target_name}.runtime_deps" } } @@ -297,7 +316,6 @@ if (rtc_include_tests) { "../call:fake_network", "../call:simulated_network", "../common_audio", - "../rtc_base:rtc_base_approved", "../rtc_base:task_queue_for_test", "../system_wrappers", "../test:field_trial", diff --git a/audio/audio_receive_stream.cc b/audio/audio_receive_stream.cc index 6f2444901b..d12edf37c3 100644 --- a/audio/audio_receive_stream.cc +++ b/audio/audio_receive_stream.cc @@ -146,7 +146,7 @@ AudioReceiveStream::AudioReceiveStream( AudioReceiveStream::~AudioReceiveStream() { RTC_DCHECK_RUN_ON(&worker_thread_checker_); - RTC_LOG(LS_INFO) << "~AudioReceiveStream: " << config_.rtp.remote_ssrc; + RTC_LOG(LS_INFO) << "~AudioReceiveStream: " << remote_ssrc(); Stop(); channel_receive_->SetAssociatedSendChannel(nullptr); channel_receive_->ResetReceiverCongestionControlObjects(); @@ -157,7 +157,7 @@ void AudioReceiveStream::RegisterWithTransport( RTC_DCHECK_RUN_ON(&packet_sequence_checker_); RTC_DCHECK(!rtp_stream_receiver_); rtp_stream_receiver_ = receiver_controller->CreateReceiver( - config_.rtp.remote_ssrc, channel_receive_.get()); + remote_ssrc(), channel_receive_.get()); } void AudioReceiveStream::UnregisterFromTransport() { @@ -170,8 +170,8 @@ void AudioReceiveStream::ReconfigureForTesting( RTC_DCHECK_RUN_ON(&worker_thread_checker_); // SSRC can't be changed mid-stream. - RTC_DCHECK_EQ(config_.rtp.remote_ssrc, config.rtp.remote_ssrc); - RTC_DCHECK_EQ(config_.rtp.local_ssrc, config.rtp.local_ssrc); + RTC_DCHECK_EQ(remote_ssrc(), config.rtp.remote_ssrc); + RTC_DCHECK_EQ(local_ssrc(), config.rtp.local_ssrc); // Configuration parameters which cannot be changed. RTC_DCHECK_EQ(config_.rtcp_send_transport, config.rtcp_send_transport); @@ -211,6 +211,11 @@ void AudioReceiveStream::Stop() { audio_state()->RemoveReceivingStream(this); } +bool AudioReceiveStream::transport_cc() const { + RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + return config_.rtp.transport_cc; +} + bool AudioReceiveStream::IsRunning() const { RTC_DCHECK_RUN_ON(&worker_thread_checker_); return playing_; @@ -265,11 +270,20 @@ void AudioReceiveStream::SetRtpExtensions( config_.rtp.extensions = std::move(extensions); } +const std::vector& AudioReceiveStream::GetRtpExtensions() const { + RTC_DCHECK_RUN_ON(&worker_thread_checker_); + return config_.rtp.extensions; +} + +RtpHeaderExtensionMap AudioReceiveStream::GetRtpExtensionMap() const { + return RtpHeaderExtensionMap(config_.rtp.extensions); +} + webrtc::AudioReceiveStream::Stats AudioReceiveStream::GetStats( bool get_and_clear_legacy_stats) const { RTC_DCHECK_RUN_ON(&worker_thread_checker_); webrtc::AudioReceiveStream::Stats stats; - stats.remote_ssrc = config_.rtp.remote_ssrc; + stats.remote_ssrc = remote_ssrc(); webrtc::CallReceiveStatistics call_stats = channel_receive_->GetRTCPStatistics(); @@ -398,7 +412,7 @@ AudioMixer::Source::AudioFrameInfo AudioReceiveStream::GetAudioFrameWithInfo( } int AudioReceiveStream::Ssrc() const { - return config_.rtp.remote_ssrc; + return remote_ssrc(); } int AudioReceiveStream::PreferredSampleRate() const { @@ -407,7 +421,7 @@ int AudioReceiveStream::PreferredSampleRate() const { uint32_t AudioReceiveStream::id() const { RTC_DCHECK_RUN_ON(&worker_thread_checker_); - return config_.rtp.remote_ssrc; + return remote_ssrc(); } absl::optional AudioReceiveStream::GetInfo() const { @@ -453,9 +467,9 @@ void AudioReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) { channel_receive_->ReceivedRTCPPacket(packet, length); } -void AudioReceiveStream::SetSyncGroup(const std::string& sync_group) { +void AudioReceiveStream::SetSyncGroup(absl::string_view sync_group) { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - config_.sync_group = sync_group; + config_.sync_group = std::string(sync_group); } void AudioReceiveStream::SetLocalSsrc(uint32_t local_ssrc) { @@ -471,9 +485,9 @@ uint32_t AudioReceiveStream::local_ssrc() const { return config_.rtp.local_ssrc; } -const webrtc::AudioReceiveStream::Config& AudioReceiveStream::config() const { - RTC_DCHECK_RUN_ON(&worker_thread_checker_); - return config_; +const std::string& AudioReceiveStream::sync_group() const { + RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + return config_.sync_group; } const AudioSendStream* AudioReceiveStream::GetAssociatedSendStreamForTesting() diff --git a/audio/audio_receive_stream.h b/audio/audio_receive_stream.h index ee518c645b..7fe942080a 100644 --- a/audio/audio_receive_stream.h +++ b/audio/audio_receive_stream.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/audio/audio_mixer.h" #include "api/neteq/neteq_factory.h" #include "api/rtp_headers.h" @@ -82,7 +83,7 @@ class AudioReceiveStream final : public webrtc::AudioReceiveStream, // webrtc::AudioReceiveStream implementation. void Start() override; void Stop() override; - const RtpConfig& rtp_config() const override { return config_.rtp; } + bool transport_cc() const override; bool IsRunning() const override; void SetDepacketizerToDecoderFrameTransformer( rtc::scoped_refptr frame_transformer) @@ -94,6 +95,8 @@ class AudioReceiveStream final : public webrtc::AudioReceiveStream, void SetFrameDecryptor(rtc::scoped_refptr frame_decryptor) override; void SetRtpExtensions(std::vector extensions) override; + const std::vector& GetRtpExtensions() const override; + RtpHeaderExtensionMap GetRtpExtensionMap() const override; webrtc::AudioReceiveStream::Stats GetStats( bool get_and_clear_legacy_stats) const override; @@ -121,19 +124,22 @@ class AudioReceiveStream final : public webrtc::AudioReceiveStream, void AssociateSendStream(AudioSendStream* send_stream); void DeliverRtcp(const uint8_t* packet, size_t length); - void SetSyncGroup(const std::string& sync_group); + void SetSyncGroup(absl::string_view sync_group); void SetLocalSsrc(uint32_t local_ssrc); uint32_t local_ssrc() const; - uint32_t remote_ssrc() const { + uint32_t remote_ssrc() const override { // The remote_ssrc member variable of config_ will never change and can be // considered const. return config_.rtp.remote_ssrc; } - const webrtc::AudioReceiveStream::Config& config() const; + // Returns a reference to the currently set sync group of the stream. + // Must be called on the packet delivery thread. + const std::string& sync_group() const; + const AudioSendStream* GetAssociatedSendStreamForTesting() const; // TODO(tommi): Remove this method. diff --git a/audio/audio_receive_stream_unittest.cc b/audio/audio_receive_stream_unittest.cc index 1d6183a1d4..a1e8c1f61f 100644 --- a/audio/audio_receive_stream_unittest.cc +++ b/audio/audio_receive_stream_unittest.cc @@ -131,6 +131,8 @@ struct ConfigHelper { EXPECT_THAT(codecs, ::testing::IsEmpty()); })); EXPECT_CALL(*channel_receive_, SetSourceTracker(_)); + EXPECT_CALL(*channel_receive_, GetLocalSsrc()) + .WillRepeatedly(Return(kLocalSsrc)); 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 08bb4e6bbf..0f6c9c10ef 100644 --- a/audio/audio_send_stream.cc +++ b/audio/audio_send_stream.cc @@ -39,7 +39,6 @@ #include "rtc_base/logging.h" #include "rtc_base/strings/audio_format_to_string.h" #include "rtc_base/task_queue.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -88,8 +87,9 @@ std::unique_ptr AudioAllocationConfig::Parser() { "rate_prio", &bitrate_priority); } -AudioAllocationConfig::AudioAllocationConfig() { - Parser()->Parse(field_trial::FindFullName(kKey)); +AudioAllocationConfig::AudioAllocationConfig( + const FieldTrialsView& field_trials) { + Parser()->Parse(field_trials.Lookup(kKey)); if (priority_bitrate_raw && !priority_bitrate.IsZero()) { RTC_LOG(LS_WARNING) << "'priority_bitrate' and '_raw' are mutually " "exclusive but both were configured."; @@ -106,28 +106,31 @@ AudioSendStream::AudioSendStream( BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, RtcpRttStats* rtcp_rtt_stats, - const absl::optional& suspended_rtp_state) - : AudioSendStream(clock, - config, - audio_state, - task_queue_factory, - rtp_transport, - bitrate_allocator, - event_log, - suspended_rtp_state, - voe::CreateChannelSend( - clock, - task_queue_factory, - config.send_transport, - rtcp_rtt_stats, - event_log, - config.frame_encryptor, - config.crypto_options, - config.rtp.extmap_allow_mixed, - config.rtcp_report_interval_ms, - config.rtp.ssrc, - config.frame_transformer, - rtp_transport->transport_feedback_observer())) {} + const absl::optional& suspended_rtp_state, + const FieldTrialsView& field_trials) + : AudioSendStream( + clock, + config, + audio_state, + task_queue_factory, + rtp_transport, + bitrate_allocator, + event_log, + suspended_rtp_state, + voe::CreateChannelSend(clock, + task_queue_factory, + config.send_transport, + rtcp_rtt_stats, + event_log, + config.frame_encryptor.get(), + config.crypto_options, + config.rtp.extmap_allow_mixed, + config.rtcp_report_interval_ms, + config.rtp.ssrc, + config.frame_transformer, + rtp_transport->transport_feedback_observer(), + field_trials), + field_trials) {} AudioSendStream::AudioSendStream( Clock* clock, @@ -138,21 +141,24 @@ AudioSendStream::AudioSendStream( BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, const absl::optional& suspended_rtp_state, - std::unique_ptr channel_send) + std::unique_ptr channel_send, + const FieldTrialsView& field_trials) : clock_(clock), + field_trials_(field_trials), rtp_transport_queue_(rtp_transport->GetWorkerQueue()), allocate_audio_without_feedback_( - field_trial::IsEnabled("WebRTC-Audio-ABWENoTWCC")), + field_trials_.IsEnabled("WebRTC-Audio-ABWENoTWCC")), enable_audio_alr_probing_( - !field_trial::IsDisabled("WebRTC-Audio-AlrProbing")), + !field_trials_.IsDisabled("WebRTC-Audio-AlrProbing")), send_side_bwe_with_overhead_( - !field_trial::IsDisabled("WebRTC-SendSideBwe-WithOverhead")), + !field_trials_.IsDisabled("WebRTC-SendSideBwe-WithOverhead")), + allocation_settings_(field_trials_), config_(Config(/*send_transport=*/nullptr)), audio_state_(audio_state), channel_send_(std::move(channel_send)), event_log_(event_log), use_legacy_overhead_calculation_( - field_trial::IsEnabled("WebRTC-Audio-LegacyOverhead")), + field_trials_.IsEnabled("WebRTC-Audio-LegacyOverhead")), bitrate_allocator_(bitrate_allocator), rtp_transport_(rtp_transport), rtp_rtcp_module_(channel_send_->GetRtpRtcp()), @@ -469,7 +475,6 @@ webrtc::AudioSendStream::Stats AudioSendStream::GetStats( stats.total_input_duration = audio_level_.TotalDuration(); } - stats.typing_noise_detected = audio_state()->typing_noise_detected(); stats.ana_statistics = channel_send_->GetANAStatistics(); AudioProcessing* ap = audio_state_->audio_processing(); @@ -640,7 +645,8 @@ bool AudioSendStream::SetupSendCodec(const Config& new_config) { AudioEncoderCopyRed::Config red_config; red_config.payload_type = *spec.red_payload_type; red_config.speech_encoder = std::move(encoder); - encoder = std::make_unique(std::move(red_config)); + encoder = std::make_unique(std::move(red_config), + field_trials_); } // Set currently known overhead (used in ANA, opus only). diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h index b40750891c..23c7213847 100644 --- a/audio/audio_send_stream.h +++ b/audio/audio_send_stream.h @@ -15,6 +15,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "audio/audio_level.h" #include "audio/channel_send.h" @@ -46,7 +47,7 @@ struct AudioAllocationConfig { absl::optional bitrate_priority; std::unique_ptr Parser(); - AudioAllocationConfig(); + explicit AudioAllocationConfig(const FieldTrialsView& field_trials); }; namespace internal { class AudioState; @@ -62,7 +63,8 @@ class AudioSendStream final : public webrtc::AudioSendStream, BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, RtcpRttStats* rtcp_rtt_stats, - const absl::optional& suspended_rtp_state); + const absl::optional& suspended_rtp_state, + const FieldTrialsView& field_trials); // For unit tests, which need to supply a mock ChannelSend. AudioSendStream(Clock* clock, const webrtc::AudioSendStream::Config& config, @@ -72,7 +74,8 @@ class AudioSendStream final : public webrtc::AudioSendStream, BitrateAllocatorInterface* bitrate_allocator, RtcEventLog* event_log, const absl::optional& suspended_rtp_state, - std::unique_ptr channel_send); + std::unique_ptr channel_send, + const FieldTrialsView& field_trials); AudioSendStream() = delete; AudioSendStream(const AudioSendStream&) = delete; @@ -160,6 +163,7 @@ class AudioSendStream final : public webrtc::AudioSendStream, RTC_RUN_ON(worker_thread_checker_); Clock* clock_; + const FieldTrialsView& field_trials_; SequenceChecker worker_thread_checker_; SequenceChecker pacer_thread_checker_; diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc index 9228611e31..0ec9964694 100644 --- a/audio/audio_send_stream_unittest.cc +++ b/audio/audio_send_stream_unittest.cc @@ -32,10 +32,10 @@ #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "rtc_base/task_queue_for_test.h" #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" #include "test/mock_audio_encoder_factory.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace test { @@ -196,7 +196,8 @@ struct ConfigHelper { Clock::GetRealTimeClock(), stream_config_, audio_state_, task_queue_factory_.get(), &rtp_transport_, &bitrate_allocator_, &event_log_, absl::nullopt, - std::unique_ptr(channel_send_))); + std::unique_ptr(channel_send_), + field_trials)); } AudioSendStream::Config& config() { return stream_config_; } @@ -321,6 +322,8 @@ struct ConfigHelper { TaskQueueForTest* worker() { return &worker_queue_; } + test::ScopedKeyValueConfig field_trials; + private: SimulatedClock clock_; std::unique_ptr task_queue_factory_; @@ -469,7 +472,6 @@ TEST(AudioSendStreamTest, GetStats) { stats.apm_statistics.residual_echo_likelihood); EXPECT_EQ(kResidualEchoLikelihoodMax, stats.apm_statistics.residual_echo_likelihood_recent_max); - EXPECT_FALSE(stats.typing_noise_detected); } } } @@ -659,10 +661,10 @@ TEST(AudioSendStreamTest, SSBweTargetInRangeRespected) { } TEST(AudioSendStreamTest, SSBweFieldTrialMinRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); auto send_stream = helper.CreateAudioSendStream(); EXPECT_CALL( *helper.channel_send(), @@ -676,10 +678,10 @@ TEST(AudioSendStreamTest, SSBweFieldTrialMinRespected) { } TEST(AudioSendStreamTest, SSBweFieldTrialMaxRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); auto send_stream = helper.CreateAudioSendStream(); EXPECT_CALL( *helper.channel_send(), @@ -693,10 +695,10 @@ TEST(AudioSendStreamTest, SSBweFieldTrialMaxRespected) { } TEST(AudioSendStreamTest, SSBweWithOverhead) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials(helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); @@ -714,11 +716,12 @@ TEST(AudioSendStreamTest, SSBweWithOverhead) { } TEST(AudioSendStreamTest, SSBweWithOverheadMinRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/" - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); @@ -734,11 +737,12 @@ TEST(AudioSendStreamTest, SSBweWithOverheadMinRespected) { } TEST(AudioSendStreamTest, SSBweWithOverheadMaxRespected) { - ScopedFieldTrials field_trials( - "WebRTC-Audio-LegacyOverhead/Disabled/" - "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); for (bool use_null_audio_processing : {false, true}) { ConfigHelper helper(true, true, use_null_audio_processing); + ScopedKeyValueConfig field_trials( + helper.field_trials, + "WebRTC-Audio-LegacyOverhead/Disabled/" + "WebRTC-Audio-Allocation/min:6kbps,max:64kbps/"); EXPECT_CALL(*helper.rtp_rtcp(), ExpectedPerPacketOverhead) .WillRepeatedly(Return(kOverheadPerPacket.bytes())); auto send_stream = helper.CreateAudioSendStream(); diff --git a/audio/audio_state.cc b/audio/audio_state.cc index 9e5b63b999..fe719470bf 100644 --- a/audio/audio_state.cc +++ b/audio/audio_state.cc @@ -28,7 +28,7 @@ namespace internal { AudioState::AudioState(const AudioState::Config& config) : config_(config), - audio_transport_(config_.audio_mixer, + audio_transport_(config_.audio_mixer.get(), config_.audio_processing.get(), config_.async_audio_processing_factory.get()) { process_thread_checker_.Detach(); @@ -50,11 +50,6 @@ AudioTransport* AudioState::audio_transport() { return &audio_transport_; } -bool AudioState::typing_noise_detected() const { - RTC_DCHECK(thread_checker_.IsCurrent()); - return audio_transport_.typing_noise_detected(); -} - void AudioState::AddReceivingStream(webrtc::AudioReceiveStream* stream) { RTC_DCHECK(thread_checker_.IsCurrent()); RTC_DCHECK_EQ(0, receiving_streams_.count(stream)); diff --git a/audio/audio_state.h b/audio/audio_state.h index 55f35511bf..b8ef4fd978 100644 --- a/audio/audio_state.h +++ b/audio/audio_state.h @@ -51,8 +51,6 @@ class AudioState : public webrtc::AudioState { return config_.audio_device_module.get(); } - bool typing_noise_detected() const; - void AddReceivingStream(webrtc::AudioReceiveStream* stream); void RemoveReceivingStream(webrtc::AudioReceiveStream* stream); diff --git a/audio/audio_transport_impl.h b/audio/audio_transport_impl.h index 89999560c6..ba067de99d 100644 --- a/audio/audio_transport_impl.h +++ b/audio/audio_transport_impl.h @@ -20,7 +20,6 @@ #include "modules/async_audio_processing/async_audio_processing.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" -#include "modules/audio_processing/typing_detection.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -86,9 +85,6 @@ class AudioTransportImpl : public AudioTransport { int send_sample_rate_hz, size_t send_num_channels); void SetStereoChannelSwapping(bool enable); - // Deprecated. - // TODO(bugs.webrtc.org/11226): Remove. - bool typing_noise_detected() const { return false; } private: void SendProcessedData(std::unique_ptr audio_frame); @@ -107,7 +103,6 @@ class AudioTransportImpl : public AudioTransport { size_t send_num_channels_ RTC_GUARDED_BY(capture_lock_) = 1; bool swap_stereo_channels_ RTC_GUARDED_BY(capture_lock_) = false; PushResampler capture_resampler_; - TypingDetection typing_detection_; // Render side. diff --git a/audio/channel_receive.cc b/audio/channel_receive.cc index f2fd34ab16..d00a9a9469 100644 --- a/audio/channel_receive.cc +++ b/audio/channel_receive.cc @@ -40,7 +40,6 @@ #include "modules/rtp_rtcp/source/rtp_rtcp_config.h" #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_minmax.h" @@ -749,12 +748,13 @@ void ChannelReceive::ReceivedRTCPPacket(const uint8_t* data, size_t length) { { MutexLock lock(&ts_stats_lock_); - ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); - absl::optional remote_to_local_clock_offset_ms = - ntp_estimator_.EstimateRemoteToLocalClockOffsetMs(); - if (remote_to_local_clock_offset_ms.has_value()) { + ntp_estimator_.UpdateRtcpTimestamp( + TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp); + absl::optional remote_to_local_clock_offset = + ntp_estimator_.EstimateRemoteToLocalClockOffset(); + if (remote_to_local_clock_offset.has_value()) { capture_clock_offset_updater_.SetRemoteToLocalClockOffset( - Int64MsToQ32x32(*remote_to_local_clock_offset_ms)); + *remote_to_local_clock_offset); } } } diff --git a/audio/channel_send.cc b/audio/channel_send.cc index d1135e5adc..05341b9f29 100644 --- a/audio/channel_send.cc +++ b/audio/channel_send.cc @@ -33,7 +33,6 @@ #include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" -#include "rtc_base/format_macros.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -43,7 +42,6 @@ #include "rtc_base/task_queue.h" #include "rtc_base/time_utils.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace webrtc { @@ -78,7 +76,8 @@ class ChannelSend : public ChannelSendInterface, int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer); + TransportFeedbackObserver* feedback_observer, + const FieldTrialsView& field_trials); ~ChannelSend() override; @@ -237,15 +236,15 @@ class ChannelSend : public ChannelSendInterface, rtc::scoped_refptr frame_transformer_delegate_ RTC_GUARDED_BY(encoder_queue_); - // Defined last to ensure that there are no running tasks when the other - // members are destroyed. - rtc::TaskQueue encoder_queue_; - const bool fixing_timestamp_stall_; mutable Mutex rtcp_counter_mutex_; RtcpPacketTypeCounter rtcp_packet_type_counter_ RTC_GUARDED_BY(rtcp_counter_mutex_); + + // Defined last to ensure that there are no running tasks when the other + // members are destroyed. + rtc::TaskQueue encoder_queue_; }; const int kTelephoneEventAttenuationdB = 10; @@ -458,7 +457,8 @@ ChannelSend::ChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer) + TransportFeedbackObserver* feedback_observer, + const FieldTrialsView& field_trials) : ssrc_(ssrc), event_log_(rtc_event_log), _timeStamp(0), // This is just an offset, RTP module will add it's own @@ -473,11 +473,11 @@ ChannelSend::ChannelSend( new RateLimiter(clock, kMaxRetransmissionWindowMs)), frame_encryptor_(frame_encryptor), crypto_options_(crypto_options), + fixing_timestamp_stall_( + field_trials.IsDisabled("WebRTC-Audio-FixTimestampStall")), encoder_queue_(task_queue_factory->CreateTaskQueue( "AudioEncoder", - TaskQueueFactory::Priority::NORMAL)), - fixing_timestamp_stall_( - !field_trial::IsDisabled("WebRTC-Audio-FixTimestampStall")) { + TaskQueueFactory::Priority::NORMAL)) { audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config())); RtpRtcpInterface::Configuration configuration; @@ -948,12 +948,13 @@ std::unique_ptr CreateChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer) { + TransportFeedbackObserver* feedback_observer, + const FieldTrialsView& field_trials) { return std::make_unique( clock, task_queue_factory, rtp_transport, rtcp_rtt_stats, rtc_event_log, frame_encryptor, crypto_options, extmap_allow_mixed, rtcp_report_interval_ms, ssrc, std::move(frame_transformer), - feedback_observer); + feedback_observer, field_trials); } } // namespace voe diff --git a/audio/channel_send.h b/audio/channel_send.h index e100725460..a555b89171 100644 --- a/audio/channel_send.h +++ b/audio/channel_send.h @@ -18,6 +18,7 @@ #include "api/audio/audio_frame.h" #include "api/audio_codecs/audio_encoder.h" #include "api/crypto/crypto_options.h" +#include "api/field_trials_view.h" #include "api/frame_transformer_interface.h" #include "api/function_view.h" #include "api/task_queue/task_queue_factory.h" @@ -135,7 +136,8 @@ std::unique_ptr CreateChannelSend( int rtcp_report_interval_ms, uint32_t ssrc, rtc::scoped_refptr frame_transformer, - TransportFeedbackObserver* feedback_observer); + TransportFeedbackObserver* feedback_observer, + const FieldTrialsView& field_trials); } // namespace voe } // namespace webrtc diff --git a/audio/remix_resample_unittest.cc b/audio/remix_resample_unittest.cc index a80476e3f1..30079bfa05 100644 --- a/audio/remix_resample_unittest.cc +++ b/audio/remix_resample_unittest.cc @@ -15,7 +15,6 @@ #include "common_audio/resampler/include/push_resampler.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "test/gtest.h" namespace webrtc { @@ -140,7 +139,7 @@ float ComputeSNR(const AudioFrame& ref_frame, best_delay = delay; } } - printf("SNR=%.1f dB at delay=%" RTC_PRIuS "\n", best_snr, best_delay); + printf("SNR=%.1f dB at delay=%zu\n", best_snr, best_delay); return best_snr; } diff --git a/audio/test/audio_stats_test.cc b/audio/test/audio_stats_test.cc index ea3327056b..8f599b0213 100644 --- a/audio/test/audio_stats_test.cc +++ b/audio/test/audio_stats_test.cc @@ -63,7 +63,6 @@ class NoLossTest : public AudioEndToEndTest { EXPECT_FALSE(send_stats.apm_statistics.echo_return_loss_enhancement); EXPECT_FALSE(send_stats.apm_statistics.residual_echo_likelihood); EXPECT_FALSE(send_stats.apm_statistics.residual_echo_likelihood_recent_max); - EXPECT_EQ(false, send_stats.typing_noise_detected); AudioReceiveStream::Stats recv_stats = receive_stream()->GetStats(/*get_and_clear_legacy_stats=*/true); diff --git a/audio/test/low_bandwidth_audio_test.py b/audio/test/low_bandwidth_audio_test.py index 386572f6e5..07065e2c8d 100755 --- a/audio/test/low_bandwidth_audio_test.py +++ b/audio/test/low_bandwidth_audio_test.py @@ -67,24 +67,16 @@ def _ParseArgs(): '--isolated-script-test-output', default=None, help='Path to output an empty JSON file which Chromium infra requires.') - parser.add_argument('--extra-test-args', - default=[], - action='append', - help='Extra args to path to the test binary.') - # Ignore Chromium-specific flags - parser.add_argument('--test-launcher-summary-output', type=str, default=None) - args = parser.parse_args() - - return args + return parser.parse_known_args() def _GetPlatform(): if sys.platform == 'win32': return 'win' - elif sys.platform == 'darwin': + if sys.platform == 'darwin': return 'mac' - elif sys.platform.startswith('linux'): + if sys.platform.startswith('linux'): return 'linux' raise AssertionError('Unknown platform %s' % sys.platform) @@ -258,14 +250,13 @@ def _ConfigurePythonPath(args): # Fail early in case the proto hasn't been built. try: - #pylint: disable=unused-variable + #pylint: disable=unused-import import histogram_pb2 except ImportError as e: - logging.exception(e) raise ImportError('Could not import histogram_pb2. You need to build the ' 'low_bandwidth_audio_perf_test target before invoking ' 'this script. Expected to find ' - 'histogram_pb2.py in %s.' % histogram_proto_path) + 'histogram_pb2.py in %s.' % histogram_proto_path) from e def main(): @@ -274,7 +265,7 @@ def main(): datefmt='%Y-%m-%d %H:%M:%S') logging.info('Invoked with %s', str(sys.argv)) - args = _ParseArgs() + args, extra_test_args = _ParseArgs() _ConfigurePythonPath(args) @@ -296,9 +287,6 @@ def main(): else: test_command = [os.path.join(args.build_dir, 'low_bandwidth_audio_test')] - if args.isolated_script_test_output: - test_command += ['--gtest_output=json:' + args.isolated_script_test_output] - analyzers = [Analyzer('pesq', _RunPesq, pesq_path, 16000)] # Check if POLQA can run at all, or skip the 48 kHz tests entirely. example_path = os.path.join(SRC_DIR, 'resources', 'voice_engine', @@ -312,7 +300,7 @@ def main(): test_process = subprocess.Popen(_LogCommand(test_command + [ '--sample_rate_hz=%d' % analyzer.sample_rate_hz, '--test_case_prefix=%s' % analyzer.name, - ] + args.extra_test_args), + ] + extra_test_args), universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -366,6 +354,10 @@ def main(): with open(args.isolated_script_test_perf_output, 'wb') as f: f.write(histograms.AsProto().SerializeToString()) + if args.isolated_script_test_output: + with open(args.isolated_script_test_output, 'w') as f: + json.dump({"version": 3}, f) + return test_process.wait() diff --git a/audio/test/pc_low_bandwidth_audio_test.cc b/audio/test/pc_low_bandwidth_audio_test.cc index 9cecc8dd41..0364670b91 100644 --- a/audio/test/pc_low_bandwidth_audio_test.cc +++ b/audio/test/pc_low_bandwidth_audio_test.cc @@ -12,6 +12,7 @@ #include "absl/flags/declare.h" #include "absl/flags/flag.h" +#include "absl/strings/string_view.h" #include "api/test/create_network_emulation_manager.h" #include "api/test/create_peerconnection_quality_test_fixture.h" #include "api/test/network_emulation_manager.h" @@ -71,14 +72,15 @@ CreateTwoNetworkLinks(NetworkEmulationManager* emulation, } std::unique_ptr -CreateTestFixture(const std::string& test_case_name, +CreateTestFixture(absl::string_view test_case_name, TimeController& time_controller, std::pair network_links, rtc::FunctionView alice_configurer, rtc::FunctionView bob_configurer) { auto fixture = webrtc_pc_e2e::CreatePeerConnectionE2EQualityTestFixture( - test_case_name, time_controller, /*audio_quality_analyzer=*/nullptr, + std::string(test_case_name), time_controller, + /*audio_quality_analyzer=*/nullptr, /*video_quality_analyzer=*/nullptr); fixture->AddPeer(network_links.first->network_dependencies(), alice_configurer); diff --git a/audio/utility/BUILD.gn b/audio/utility/BUILD.gn index 933553d81b..983b6286e4 100644 --- a/audio/utility/BUILD.gn +++ b/audio/utility/BUILD.gn @@ -26,7 +26,8 @@ rtc_library("audio_frame_operations") { "../../api/audio:audio_frame_api", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:safe_conversions", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] @@ -44,7 +45,9 @@ if (rtc_include_tests) { ":audio_frame_operations", "../../api/audio:audio_frame_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:stringutils", "../../test:field_trial", "../../test:test_support", "//testing/gtest", diff --git a/audio/voip/BUILD.gn b/audio/voip/BUILD.gn index 5311d7242b..b9397336b5 100644 --- a/audio/voip/BUILD.gn +++ b/audio/voip/BUILD.gn @@ -48,9 +48,9 @@ rtc_library("audio_channel") { "../../modules/rtp_rtcp:rtp_rtcp_format", "../../modules/utility", "../../rtc_base:criticalsection", + "../../rtc_base:location", "../../rtc_base:logging", "../../rtc_base:refcount", - "../../rtc_base:rtc_base_approved", ] } diff --git a/audio/voip/audio_ingress.cc b/audio/voip/audio_ingress.cc index 8aa552bb28..71026e84e0 100644 --- a/audio/voip/audio_ingress.cc +++ b/audio/voip/audio_ingress.cc @@ -226,7 +226,8 @@ void AudioIngress::ReceivedRTCPPacket( { MutexLock lock(&lock_); - ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); + ntp_estimator_.UpdateRtcpTimestamp( + TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp); } } diff --git a/call/BUILD.gn b/call/BUILD.gn index b2b56b273c..ba0da32bca 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -42,6 +42,7 @@ rtc_library("call_interfaces") { ":rtp_interfaces", ":video_stream_api", "../api:fec_controller_api", + "../api:field_trials_view", "../api:frame_transformer_interface", "../api:network_state_predictor_api", "../api:rtc_error", @@ -60,7 +61,6 @@ rtc_library("call_interfaces") { "../api/task_queue", "../api/transport:bitrate_settings", "../api/transport:network_control", - "../api/transport:webrtc_key_value_config", "../modules/async_audio_processing", "../modules/audio_device", "../modules/audio_processing", @@ -71,7 +71,9 @@ rtc_library("call_interfaces") { "../rtc_base", "../rtc_base:audio_format_to_string", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:refcount", + "../rtc_base:stringutils", "../rtc_base/network:sent_packet", ] absl_deps = [ @@ -106,6 +108,7 @@ rtc_library("rtp_interfaces") { deps = [ "../api:array_view", "../api:fec_controller_api", + "../api:field_trials_view", "../api:frame_transformer_interface", "../api:network_state_predictor_api", "../api:rtp_headers", @@ -114,14 +117,13 @@ rtc_library("rtp_interfaces") { "../api/rtc_event_log", "../api/transport:bitrate_settings", "../api/transport:network_control", - "../api/transport:webrtc_key_value_config", "../api/units:timestamp", "../common_video:frame_counts", "../modules/rtp_rtcp:rtp_rtcp_format", "../modules/utility", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_task_queue", + "../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", @@ -147,7 +149,8 @@ rtc_library("rtp_receiver") { "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:stringutils", "../rtc_base/containers:flat_map", "../rtc_base/containers:flat_set", ] @@ -174,6 +177,7 @@ rtc_library("rtp_sender") { "../api:array_view", "../api:bitrate_allocation", "../api:fec_controller_api", + "../api:field_trials_view", "../api:network_state_predictor_api", "../api:rtp_parameters", "../api:sequence_checker", @@ -182,7 +186,6 @@ rtc_library("rtp_sender") { "../api/transport:field_trial_based_config", "../api/transport:goog_cc", "../api/transport:network_control", - "../api/transport:webrtc_key_value_config", "../api/units:data_rate", "../api/units:time_delta", "../api/units:timestamp", @@ -205,9 +208,15 @@ rtc_library("rtp_sender") { "../modules/video_coding:video_codec_interface", "../rtc_base", "../rtc_base:checks", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:race_checker", + "../rtc_base:random", "../rtc_base:rate_limiter", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_task_queue", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:repeating_task", ] @@ -233,7 +242,6 @@ rtc_library("bitrate_configurator") { "../api/transport:bitrate_settings", "../api/units:data_rate", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -250,7 +258,7 @@ rtc_library("bitrate_allocator") { "../api/units:data_rate", "../api/units:time_delta", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", "../rtc_base:safe_minmax", "../rtc_base/system:no_unique_address", "../system_wrappers", @@ -286,6 +294,7 @@ rtc_library("call") { "../api:array_view", "../api:callfactory_api", "../api:fec_controller_api", + "../api:field_trials_view", "../api:rtp_headers", "../api:rtp_parameters", "../api:sequence_checker", @@ -307,10 +316,16 @@ rtc_library("call") { "../modules/utility", "../modules/video_coding", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:rate_limiter", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_task_queue", "../rtc_base:safe_minmax", + "../rtc_base:stringutils", + "../rtc_base:timeutils", "../rtc_base/experiments:field_trial_parser", "../rtc_base/network:sent_packet", "../rtc_base/system:no_unique_address", @@ -336,6 +351,7 @@ rtc_source_set("receive_stream_interface") { "../api:scoped_refptr", "../api/crypto:frame_decryptor_interface", "../api/transport/rtp:rtp_source", + "../modules/rtp_rtcp:rtp_rtcp_format", ] } @@ -366,7 +382,6 @@ rtc_library("video_stream_api") { "../common_video:frame_counts", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -385,7 +400,9 @@ rtc_library("simulated_network") { "../api/units:time_delta", "../api/units:timestamp", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", + "../rtc_base:race_checker", + "../rtc_base:random", "../rtc_base/synchronization:mutex", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -414,7 +431,8 @@ rtc_library("fake_network") { "../api:transport_api", "../modules/utility", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base/synchronization:mutex", "../system_wrappers", ] @@ -478,9 +496,13 @@ if (rtc_include_tests) { "../modules/video_coding:video_codec_interface", "../rtc_base:checks", "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:random", "../rtc_base:rate_limiter", - "../rtc_base:rtc_base_approved", + "../rtc_base:rtc_event", + "../rtc_base:safe_conversions", "../rtc_base:task_queue_for_test", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../system_wrappers", "../test:audio_codec_mocks", @@ -491,6 +513,7 @@ if (rtc_include_tests) { "../test:field_trial", "../test:mock_frame_transformer", "../test:mock_transport", + "../test:scoped_key_value_config", "../test:test_common", "../test:test_support", "../test:video_test_common", @@ -504,6 +527,7 @@ if (rtc_include_tests) { absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", "//third_party/abseil-cpp/absl/types:variant", ] @@ -541,9 +565,14 @@ if (rtc_include_tests) { "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:platform_thread", + "../rtc_base:rtc_event", + "../rtc_base:stringutils", "../rtc_base:task_queue_for_test", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:repeating_task", diff --git a/call/adaptation/BUILD.gn b/call/adaptation/BUILD.gn index 10a46a3d43..2cb8093860 100644 --- a/call/adaptation/BUILD.gn +++ b/call/adaptation/BUILD.gn @@ -32,6 +32,7 @@ rtc_library("resource_adaptation") { "video_stream_input_state_provider.h", ] deps = [ + "../../api:field_trials_view", "../../api:rtp_parameters", "../../api:scoped_refptr", "../../api:sequence_checker", @@ -43,8 +44,12 @@ rtc_library("resource_adaptation") { "../../api/video_codecs:video_codecs_api", "../../modules/video_coding:video_coding_utility", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:refcount", "../../rtc_base:rtc_task_queue", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", "../../rtc_base/experiments:balanced_degradation_settings", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:no_unique_address", @@ -80,12 +85,14 @@ if (rtc_include_tests) { "../../api/video_codecs:video_codecs_api", "../../rtc_base:checks", "../../rtc_base:gunit_helpers", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_task_queue", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", "../../rtc_base/synchronization:mutex", "../../test:field_trial", "../../test:rtc_expect_death", + "../../test:scoped_key_value_config", "../../test:test_support", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -112,7 +119,7 @@ if (rtc_include_tests) { "../../api/adaptation:resource_adaptation_api", "../../api/task_queue:task_queue", "../../api/video:video_stream_encoder", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", "../../rtc_base/task_utils:to_queued_task", "../../test:test_support", ] diff --git a/call/adaptation/resource_adaptation_processor.cc b/call/adaptation/resource_adaptation_processor.cc index 66e6f0c36e..a89d64b412 100644 --- a/call/adaptation/resource_adaptation_processor.cc +++ b/call/adaptation/resource_adaptation_processor.cc @@ -113,7 +113,7 @@ void ResourceAdaptationProcessor::AddResource( << "Resource \"" << resource->Name() << "\" was already registered."; resources_.push_back(resource); } - resource->SetResourceListener(resource_listener_delegate_); + resource->SetResourceListener(resource_listener_delegate_.get()); RTC_LOG(LS_INFO) << "Registered resource \"" << resource->Name() << "\"."; } diff --git a/call/adaptation/resource_adaptation_processor_unittest.cc b/call/adaptation/resource_adaptation_processor_unittest.cc index 705223ab71..da2bc947b9 100644 --- a/call/adaptation/resource_adaptation_processor_unittest.cc +++ b/call/adaptation/resource_adaptation_processor_unittest.cc @@ -23,6 +23,7 @@ #include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_queue_for_test.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -90,7 +91,8 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { other_resource_(FakeResource::Create("OtherFakeResource")), video_stream_adapter_( std::make_unique(&input_state_provider_, - &frame_rate_provider_)), + &frame_rate_provider_, + field_trials_)), processor_(std::make_unique( video_stream_adapter_.get())) { video_stream_adapter_->AddRestrictionsListener(&restrictions_listener_); @@ -133,6 +135,7 @@ class ResourceAdaptationProcessorTest : public ::testing::Test { } protected: + webrtc::test::ScopedKeyValueConfig field_trials_; FakeFrameRateProvider frame_rate_provider_; VideoStreamInputStateProvider input_state_provider_; rtc::scoped_refptr resource_; diff --git a/call/adaptation/video_stream_adapter.cc b/call/adaptation/video_stream_adapter.cc index ce1c300a72..f30a4d7abb 100644 --- a/call/adaptation/video_stream_adapter.cc +++ b/call/adaptation/video_stream_adapter.cc @@ -203,9 +203,11 @@ const VideoAdaptationCounters& Adaptation::counters() const { VideoStreamAdapter::VideoStreamAdapter( VideoStreamInputStateProvider* input_state_provider, - VideoStreamEncoderObserver* encoder_stats_observer) + VideoStreamEncoderObserver* encoder_stats_observer, + const FieldTrialsView& field_trials) : input_state_provider_(input_state_provider), encoder_stats_observer_(encoder_stats_observer), + balanced_settings_(field_trials), adaptation_validation_id_(0), degradation_preference_(DegradationPreference::DISABLED), awaiting_frame_size_change_(absl::nullopt) { diff --git a/call/adaptation/video_stream_adapter.h b/call/adaptation/video_stream_adapter.h index 7bf424a17e..92a5aec058 100644 --- a/call/adaptation/video_stream_adapter.h +++ b/call/adaptation/video_stream_adapter.h @@ -18,6 +18,7 @@ #include "absl/types/optional.h" #include "absl/types/variant.h" #include "api/adaptation/resource.h" +#include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/video/video_adaptation_counters.h" #include "api/video/video_stream_encoder_observer.h" @@ -123,7 +124,8 @@ class Adaptation final { class VideoStreamAdapter { public: VideoStreamAdapter(VideoStreamInputStateProvider* input_state_provider, - VideoStreamEncoderObserver* encoder_stats_observer); + VideoStreamEncoderObserver* encoder_stats_observer, + const FieldTrialsView& field_trials); ~VideoStreamAdapter(); VideoSourceRestrictions source_restrictions() const; diff --git a/call/adaptation/video_stream_adapter_unittest.cc b/call/adaptation/video_stream_adapter_unittest.cc index aba9cf1f29..8f29194254 100644 --- a/call/adaptation/video_stream_adapter_unittest.cc +++ b/call/adaptation/video_stream_adapter_unittest.cc @@ -27,9 +27,9 @@ #include "call/adaptation/video_source_restrictions.h" #include "call/adaptation/video_stream_input_state.h" #include "rtc_base/string_encode.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "test/testsupport/rtc_expect_death.h" namespace webrtc { @@ -153,10 +153,12 @@ class VideoStreamAdapterTest : public ::testing::Test { VideoStreamAdapterTest() : field_trials_(BalancedFieldTrialConfig()), resource_(FakeResource::Create("FakeResource")), - adapter_(&input_state_provider_, &encoder_stats_observer_) {} + adapter_(&input_state_provider_, + &encoder_stats_observer_, + field_trials_) {} protected: - webrtc::test::ScopedFieldTrials field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; FakeVideoStreamInputStateProvider input_state_provider_; rtc::scoped_refptr resource_; testing::StrictMock encoder_stats_observer_; @@ -919,9 +921,11 @@ TEST_F(VideoStreamAdapterTest, AdaptationConstraintDisallowsAdaptationsUp) { TEST(VideoStreamAdapterDeathTest, SetDegradationPreferenceInvalidatesAdaptations) { + webrtc::test::ScopedKeyValueConfig field_trials; FakeVideoStreamInputStateProvider input_state_provider; testing::StrictMock encoder_stats_observer_; - VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_); + VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, + field_trials); adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_FRAMERATE); input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); Adaptation adaptation = adapter.GetAdaptationDown(); @@ -930,9 +934,11 @@ TEST(VideoStreamAdapterDeathTest, } TEST(VideoStreamAdapterDeathTest, AdaptDownInvalidatesAdaptations) { + webrtc::test::ScopedKeyValueConfig field_trials; FakeVideoStreamInputStateProvider input_state_provider; testing::StrictMock encoder_stats_observer_; - VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_); + VideoStreamAdapter adapter(&input_state_provider, &encoder_stats_observer_, + field_trials); adapter.SetDegradationPreference(DegradationPreference::MAINTAIN_RESOLUTION); input_state_provider.SetInputState(1280 * 720, 30, kDefaultMinPixelsPerFrame); Adaptation adaptation = adapter.GetAdaptationDown(); diff --git a/call/audio_receive_stream.h b/call/audio_receive_stream.h index 17691f7021..45f2feb795 100644 --- a/call/audio_receive_stream.h +++ b/call/audio_receive_stream.h @@ -108,7 +108,7 @@ class AudioReceiveStream : public MediaReceiveStream { std::string ToString() const; // Receive-stream specific RTP settings. - struct Rtp : public RtpConfig { + struct Rtp : public ReceiveStreamRtpConfig { Rtp(); ~Rtp(); @@ -194,6 +194,17 @@ class AudioReceiveStream : public MediaReceiveStream { // Returns current value of base minimum delay in milliseconds. virtual int GetBaseMinimumPlayoutDelayMs() const = 0; + // Synchronization source (stream identifier) to be received. + // This member will not change mid-stream and can be assumed to be const + // post initialization. + virtual uint32_t remote_ssrc() const = 0; + + // Access the currently set rtp extensions. Must be called on the packet + // delivery thread. + // TODO(tommi): This is currently only called from + // `WebRtcAudioReceiveStream::GetRtpParameters()`. See if we can remove it. + virtual const std::vector& GetRtpExtensions() const = 0; + protected: virtual ~AudioReceiveStream() {} }; diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h index e38a47f871..25d086beaa 100644 --- a/call/audio_send_stream.h +++ b/call/audio_send_stream.h @@ -59,7 +59,6 @@ class AudioSendStream : public AudioSender { // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy double total_input_energy = 0.0; double total_input_duration = 0.0; - bool typing_noise_detected = false; ANAStats ana_statistics; AudioProcessingStats apm_statistics; diff --git a/call/bitrate_allocator.cc b/call/bitrate_allocator.cc index 1693661ef5..2684a1650e 100644 --- a/call/bitrate_allocator.cc +++ b/call/bitrate_allocator.cc @@ -23,7 +23,6 @@ #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_minmax.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace webrtc { diff --git a/call/bitrate_estimator_tests.cc b/call/bitrate_estimator_tests.cc index 4634f6e147..424cf0b038 100644 --- a/call/bitrate_estimator_tests.cc +++ b/call/bitrate_estimator_tests.cc @@ -12,6 +12,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/test/create_frame_generator.h" #include "call/call.h" #include "call/fake_network_pipe.h" @@ -50,13 +51,17 @@ class LogObserver { class Callback : public rtc::LogSink { public: void OnLogMessage(const std::string& message) override { + OnLogMessage(absl::string_view(message)); + } + + void OnLogMessage(absl::string_view message) override { MutexLock lock(&mutex_); // Ignore log lines that are due to missing AST extensions, these are // logged when we switch back from AST to TOF until the wrapping bitrate // estimator gives up on using AST. - if (message.find("BitrateEstimator") != std::string::npos && - message.find("packet is missing") == std::string::npos) { - received_log_lines_.push_back(message); + if (message.find("BitrateEstimator") != absl::string_view::npos && + message.find("packet is missing") == absl::string_view::npos) { + received_log_lines_.push_back(std::string(message)); } int num_popped = 0; @@ -66,7 +71,7 @@ class LogObserver { received_log_lines_.pop_front(); expected_log_lines_.pop_front(); num_popped++; - EXPECT_TRUE(a.find(b) != std::string::npos) << a << " != " << b; + EXPECT_TRUE(a.find(b) != absl::string_view::npos) << a << " != " << b; } if (expected_log_lines_.empty()) { if (num_popped > 0) { diff --git a/call/call.cc b/call/call.cc index bb62365cd4..ed5ee1bc18 100644 --- a/call/call.cc +++ b/call/call.cc @@ -61,7 +61,6 @@ #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/cpu_info.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" #include "video/call_stats2.h" #include "video/send_delay_stats.h" @@ -80,15 +79,14 @@ bool SendPeriodicFeedback(const std::vector& extensions) { return true; } -bool UseSendSideBwe(const ReceiveStream::RtpConfig& rtp) { - if (!rtp.transport_cc) - return false; - for (const auto& extension : rtp.extensions) { - if (extension.uri == RtpExtension::kTransportSequenceNumberUri || - extension.uri == RtpExtension::kTransportSequenceNumberV2Uri) - return true; - } - return false; +bool HasTransportSequenceNumber(const RtpHeaderExtensionMap& map) { + return map.IsRegistered(kRtpExtensionTransportSequenceNumber) || + map.IsRegistered(kRtpExtensionTransportSequenceNumber02); +} + +bool UseSendSideBwe(const ReceiveStream* stream) { + return stream->transport_cc() && + HasTransportSequenceNumber(stream->GetRtpExtensionMap()); } const int* FindKeyByValue(const std::map& m, int v) { @@ -238,7 +236,7 @@ class Call final : public webrtc::Call, webrtc::VideoReceiveStream* receive_stream) override; FlexfecReceiveStream* CreateFlexfecReceiveStream( - const FlexfecReceiveStream::Config& config) override; + const FlexfecReceiveStream::Config config) override; void DestroyFlexfecReceiveStream( FlexfecReceiveStream* receive_stream) override; @@ -248,7 +246,7 @@ class Call final : public webrtc::Call, Stats GetStats() const override; - const WebRtcKeyValueConfig& trials() const override; + const FieldTrialsView& trials() const override; TaskQueueBase* network_thread() const override; TaskQueueBase* worker_thread() const override; @@ -380,7 +378,7 @@ class Call final : public webrtc::Call, const std::unique_ptr bitrate_allocator_; const Call::Config config_ RTC_GUARDED_BY(worker_thread_); // Maps to config_.trials, can be used from any thread via `trials()`. - const WebRtcKeyValueConfig& trials_; + const FieldTrialsView& trials_; NetworkState audio_network_state_ RTC_GUARDED_BY(worker_thread_); NetworkState video_network_state_ RTC_GUARDED_BY(worker_thread_); @@ -824,7 +822,8 @@ Call::Call(Clock* clock, absl::bind_front(&PacketRouter::SendRemb, transport_send->packet_router()), /*network_state_estimator=*/nullptr), - receive_time_calculator_(ReceiveTimeCalculator::CreateFromFieldTrial()), + receive_time_calculator_( + ReceiveTimeCalculator::CreateFromFieldTrial(*config.trials)), video_send_delay_stats_(new SendDelayStats(clock_)), start_of_call_(clock_->CurrentTime()), transport_send_ptr_(transport_send.get()), @@ -915,7 +914,7 @@ webrtc::AudioSendStream* Call::CreateAudioSendStream( AudioSendStream* send_stream = new AudioSendStream( clock_, config, config_.audio_state, task_queue_factory_, transport_send_.get(), bitrate_allocator_.get(), event_log_, - call_stats_->AsRtcpRttStats(), suspended_rtp_state); + call_stats_->AsRtcpRttStats(), suspended_rtp_state, trials()); RTC_DCHECK(audio_send_ssrcs_.find(config.rtp.ssrc) == audio_send_ssrcs_.end()); audio_send_ssrcs_[config.rtp.ssrc] = send_stream; @@ -1009,8 +1008,8 @@ void Call::DestroyAudioReceiveStream( audio_receive_stream->UnregisterFromTransport(); uint32_t ssrc = audio_receive_stream->remote_ssrc(); - const AudioReceiveStream::Config& config = audio_receive_stream->config(); - receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(config.rtp)) + receive_side_cc_ + .GetRemoteBitrateEstimator(UseSendSideBwe(audio_receive_stream)) ->RemoveStream(ssrc); audio_receive_streams_.erase(audio_receive_stream); @@ -1018,7 +1017,7 @@ void Call::DestroyAudioReceiveStream( // After calling erase(), call ConfigureSync. This will clear associated // video streams or associate them with a different audio stream if one exists // for this sync_group. - ConfigureSync(audio_receive_stream->config().sync_group); + ConfigureSync(audio_receive_stream->sync_group()); UnregisterReceiveStream(ssrc); @@ -1056,7 +1055,8 @@ webrtc::VideoSendStream* Call::CreateVideoSendStream( call_stats_->AsRtcpRttStats(), transport_send_.get(), bitrate_allocator_.get(), video_send_delay_stats_.get(), event_log_, std::move(config), std::move(encoder_config), suspended_video_send_ssrcs_, - suspended_video_payload_states_, std::move(fec_controller)); + suspended_video_payload_states_, std::move(fec_controller), + *config_.trials); for (uint32_t ssrc : ssrcs) { RTC_DCHECK(video_send_ssrcs_.find(ssrc) == video_send_ssrcs_.end()); @@ -1153,21 +1153,20 @@ webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream( VideoReceiveStream2* receive_stream = new VideoReceiveStream2( task_queue_factory_, this, num_cpu_cores_, transport_send_->packet_router(), std::move(configuration), - call_stats_.get(), clock_, new VCMTiming(clock_), + call_stats_.get(), clock_, std::make_unique(clock_, trials()), &nack_periodic_processor_, decode_sync_.get()); // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // thread. receive_stream->RegisterWithTransport(&video_receiver_controller_); - const webrtc::VideoReceiveStream::Config::Rtp& rtp = receive_stream->rtp(); - if (rtp.rtx_ssrc) { + if (receive_stream->rtx_ssrc()) { // We record identical config for the rtx stream as for the main // stream. Since the transport_send_cc negotiation is per payload // type, we may get an incorrect value for the rtx stream, but // that is unlikely to matter in practice. - RegisterReceiveStream(rtp.rtx_ssrc, receive_stream); + RegisterReceiveStream(receive_stream->rtx_ssrc(), receive_stream); } - RegisterReceiveStream(rtp.remote_ssrc, receive_stream); + RegisterReceiveStream(receive_stream->remote_ssrc(), receive_stream); video_receive_streams_.insert(receive_stream); ConfigureSync(receive_stream->sync_group()); @@ -1187,47 +1186,42 @@ void Call::DestroyVideoReceiveStream( // TODO(bugs.webrtc.org/11993): Unregister on the network thread. receive_stream_impl->UnregisterFromTransport(); - const webrtc::VideoReceiveStream::Config::Rtp& rtp = - receive_stream_impl->rtp(); - // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a // separate SSRC there can be either one or two. - UnregisterReceiveStream(rtp.remote_ssrc); - if (rtp.rtx_ssrc) { - UnregisterReceiveStream(rtp.rtx_ssrc); + UnregisterReceiveStream(receive_stream_impl->remote_ssrc()); + + if (receive_stream_impl->rtx_ssrc()) { + UnregisterReceiveStream(receive_stream_impl->rtx_ssrc()); } video_receive_streams_.erase(receive_stream_impl); ConfigureSync(receive_stream_impl->sync_group()); - receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(rtp)) - ->RemoveStream(rtp.remote_ssrc); + receive_side_cc_ + .GetRemoteBitrateEstimator(UseSendSideBwe(receive_stream_impl)) + ->RemoveStream(receive_stream_impl->remote_ssrc()); UpdateAggregateNetworkState(); delete receive_stream_impl; } FlexfecReceiveStream* Call::CreateFlexfecReceiveStream( - const FlexfecReceiveStream::Config& config) { + const FlexfecReceiveStream::Config config) { TRACE_EVENT0("webrtc", "Call::CreateFlexfecReceiveStream"); RTC_DCHECK_RUN_ON(worker_thread_); - RecoveredPacketReceiver* recovered_packet_receiver = this; - - FlexfecReceiveStreamImpl* receive_stream; - // Unlike the video and audio receive streams, FlexfecReceiveStream implements // RtpPacketSinkInterface itself, and hence its constructor passes its `this` // pointer to video_receiver_controller_->CreateStream(). Calling the // constructor while on the worker thread ensures that we don't call // OnRtpPacket until the constructor is finished and the object is // in a valid state, since OnRtpPacket runs on the same thread. - receive_stream = new FlexfecReceiveStreamImpl( - clock_, config, recovered_packet_receiver, call_stats_->AsRtcpRttStats()); + FlexfecReceiveStreamImpl* receive_stream = new FlexfecReceiveStreamImpl( + clock_, std::move(config), this, call_stats_->AsRtcpRttStats()); // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // thread. receive_stream->RegisterWithTransport(&video_receiver_controller_); - RegisterReceiveStream(config.rtp.remote_ssrc, receive_stream); + RegisterReceiveStream(receive_stream->remote_ssrc(), receive_stream); // TODO(brandtr): Store config in RtcEventLog here. @@ -1243,16 +1237,16 @@ void Call::DestroyFlexfecReceiveStream(FlexfecReceiveStream* receive_stream) { // TODO(bugs.webrtc.org/11993): Unregister on the network thread. receive_stream_impl->UnregisterFromTransport(); - RTC_DCHECK(receive_stream != nullptr); - const FlexfecReceiveStream::RtpConfig& rtp = receive_stream->rtp_config(); - UnregisterReceiveStream(rtp.remote_ssrc); + auto ssrc = receive_stream_impl->remote_ssrc(); + UnregisterReceiveStream(ssrc); // Remove all SSRCs pointing to the FlexfecReceiveStreamImpl to be // destroyed. - receive_side_cc_.GetRemoteBitrateEstimator(UseSendSideBwe(rtp)) - ->RemoveStream(rtp.remote_ssrc); + receive_side_cc_ + .GetRemoteBitrateEstimator(UseSendSideBwe(receive_stream_impl)) + ->RemoveStream(ssrc); - delete receive_stream; + delete receive_stream_impl; } void Call::AddAdaptationResource(rtc::scoped_refptr resource) { @@ -1294,7 +1288,7 @@ Call::Stats Call::GetStats() const { return stats; } -const WebRtcKeyValueConfig& Call::trials() const { +const FieldTrialsView& Call::trials() const { return trials_; } @@ -1470,7 +1464,7 @@ AudioReceiveStream* Call::FindAudioStreamForSyncGroup( RTC_DCHECK_RUN_ON(&receive_11993_checker_); if (!sync_group.empty()) { for (AudioReceiveStream* stream : audio_receive_streams_) { - if (stream->config().sync_group == sync_group) + if (stream->sync_group() == sync_group) return stream; } } @@ -1690,11 +1684,10 @@ bool Call::IdentifyReceivedPacket(RtpPacketReceived& packet, return false; } - packet.IdentifyExtensions( - RtpHeaderExtensionMap(it->second->rtp_config().extensions)); + packet.IdentifyExtensions(it->second->GetRtpExtensionMap()); if (use_send_side_bwe) { - *use_send_side_bwe = UseSendSideBwe(it->second->rtp_config()); + *use_send_side_bwe = UseSendSideBwe(it->second); } return true; diff --git a/call/call.h b/call/call.h index 11451c5c82..9d6d4ee11a 100644 --- a/call/call.h +++ b/call/call.h @@ -127,7 +127,7 @@ class Call { // protected by a FlexfecReceiveStream, the latter should be created before // the former. virtual FlexfecReceiveStream* CreateFlexfecReceiveStream( - const FlexfecReceiveStream::Config& config) = 0; + const FlexfecReceiveStream::Config config) = 0; virtual void DestroyFlexfecReceiveStream( FlexfecReceiveStream* receive_stream) = 0; @@ -174,7 +174,7 @@ class Call { virtual void SetClientBitratePreferences( const BitrateSettings& preferences) = 0; - virtual const WebRtcKeyValueConfig& trials() const = 0; + virtual const FieldTrialsView& trials() const = 0; virtual TaskQueueBase* network_thread() const = 0; virtual TaskQueueBase* worker_thread() const = 0; diff --git a/call/call_config.h b/call/call_config.h index ef505a4b0a..3072fa452f 100644 --- a/call/call_config.h +++ b/call/call_config.h @@ -11,6 +11,7 @@ #define CALL_CALL_CONFIG_H_ #include "api/fec_controller.h" +#include "api/field_trials_view.h" #include "api/metronome/metronome.h" #include "api/neteq/neteq_factory.h" #include "api/network_state_predictor.h" @@ -18,7 +19,6 @@ #include "api/task_queue/task_queue_factory.h" #include "api/transport/bitrate_settings.h" #include "api/transport/network_control.h" -#include "api/transport/webrtc_key_value_config.h" #include "call/audio_state.h" #include "call/rtp_transport_config.h" #include "call/rtp_transport_controller_send_factory_interface.h" @@ -70,7 +70,7 @@ struct CallConfig { // Key-value mapping of internal configurations to apply, // e.g. field trials. - const WebRtcKeyValueConfig* trials = nullptr; + const FieldTrialsView* trials = nullptr; TaskQueueBase* const network_task_queue_ = nullptr; // RtpTransportControllerSend to use for this call. diff --git a/call/call_factory.cc b/call/call_factory.cc index a3079eb7b2..6d4b2aa211 100644 --- a/call/call_factory.cc +++ b/call/call_factory.cc @@ -31,7 +31,7 @@ namespace webrtc { namespace { using TimeScopedNetworkConfig = DegradedCall::TimeScopedNetworkConfig; -bool ParseConfigParam(const WebRtcKeyValueConfig& trials, +bool ParseConfigParam(const FieldTrialsView& trials, absl::string_view exp_name, int* field) { std::string group = trials.Lookup(exp_name); @@ -42,7 +42,7 @@ bool ParseConfigParam(const WebRtcKeyValueConfig& trials, } absl::optional ParseDegradationConfig( - const WebRtcKeyValueConfig& trials, + const FieldTrialsView& trials, bool send) { std::string exp_prefix = "WebRTCFakeNetwork"; if (send) { @@ -80,7 +80,7 @@ absl::optional ParseDegradationConfig( } std::vector GetNetworkConfigs( - const WebRtcKeyValueConfig& trials, + const FieldTrialsView& trials, bool send) { FieldTrialStructList trials_list( {FieldTrialStructMember("queue_length_packets", @@ -165,8 +165,7 @@ Call* CallFactory::CreateCall(const Call::Config& config) { config.rtp_transport_controller_send_factory->Create( transportConfig, Clock::GetRealTimeClock(), ProcessThread::Create("PacerThread")))), - send_degradation_configs, receive_degradation_configs, - config.task_queue_factory); + send_degradation_configs, receive_degradation_configs); } if (!module_thread_) { diff --git a/call/call_perf_tests.cc b/call/call_perf_tests.cc index f4a20b82fe..6acebf2bb7 100644 --- a/call/call_perf_tests.cc +++ b/call/call_perf_tests.cc @@ -766,8 +766,8 @@ TEST_F(CallPerfTest, MAYBE_KeepsHighBitrateWhenReconfiguringSender) { // We get lower bitrate than expected by this test if the following field // trial is enabled. - test::ScopedFieldTrials field_trials( - "WebRTC-SendSideBwe-WithOverhead/Disabled/"); + test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-SendSideBwe-WithOverhead/Disabled/"); class VideoStreamFactory : public VideoEncoderConfig::VideoStreamFactoryInterface { diff --git a/call/degraded_call.cc b/call/degraded_call.cc index 1eb2275d20..3790c78927 100644 --- a/call/degraded_call.cc +++ b/call/degraded_call.cc @@ -18,13 +18,13 @@ namespace webrtc { DegradedCall::FakeNetworkPipeOnTaskQueue::FakeNetworkPipeOnTaskQueue( - TaskQueueFactory* task_queue_factory, + TaskQueueBase* task_queue, + const ScopedTaskSafety& task_safety, Clock* clock, std::unique_ptr network_behavior) : clock_(clock), - task_queue_(task_queue_factory->CreateTaskQueue( - "DegradedSendQueue", - TaskQueueFactory::Priority::NORMAL)), + task_queue_(task_queue), + task_safety_(task_safety), pipe_(clock, std::move(network_behavior)) {} void DegradedCall::FakeNetworkPipeOnTaskQueue::SendRtp( @@ -61,21 +61,22 @@ bool DegradedCall::FakeNetworkPipeOnTaskQueue::Process() { return false; } - task_queue_.PostTask([this, time_to_next]() { - RTC_DCHECK_RUN_ON(&task_queue_); + task_queue_->PostTask(ToQueuedTask(task_safety_, [this, time_to_next] { + RTC_DCHECK_RUN_ON(task_queue_); int64_t next_process_time = *time_to_next + clock_->TimeInMilliseconds(); if (!next_process_ms_ || next_process_time < *next_process_ms_) { next_process_ms_ = next_process_time; - task_queue_.PostDelayedHighPrecisionTask( - [this]() { - RTC_DCHECK_RUN_ON(&task_queue_); - if (!Process()) { - next_process_ms_.reset(); - } - }, + task_queue_->PostDelayedHighPrecisionTask( + ToQueuedTask(task_safety_, + [this] { + RTC_DCHECK_RUN_ON(task_queue_); + if (!Process()) { + next_process_ms_.reset(); + } + }), *time_to_next); } - }); + })); return true; } @@ -128,11 +129,9 @@ bool DegradedCall::FakeNetworkPipeTransportAdapter::SendRtcp( DegradedCall::DegradedCall( std::unique_ptr call, const std::vector& send_configs, - const std::vector& receive_configs, - TaskQueueFactory* task_queue_factory) + const std::vector& receive_configs) : clock_(Clock::GetRealTimeClock()), call_(std::move(call)), - task_queue_factory_(task_queue_factory), send_config_index_(0), send_configs_(send_configs), send_simulated_network_(nullptr), @@ -154,7 +153,7 @@ DegradedCall::DegradedCall( auto network = std::make_unique(send_configs_[0]); send_simulated_network_ = network.get(); send_pipe_ = std::make_unique( - task_queue_factory_, clock_, std::move(network)); + call_->network_thread(), task_safety_, clock_, std::move(network)); if (send_configs_.size() > 1) { call_->network_thread()->PostDelayedTask( ToQueuedTask(task_safety_, [this] { UpdateSendNetworkConfig(); }), @@ -248,8 +247,8 @@ void DegradedCall::DestroyVideoReceiveStream( } FlexfecReceiveStream* DegradedCall::CreateFlexfecReceiveStream( - const FlexfecReceiveStream::Config& config) { - return call_->CreateFlexfecReceiveStream(config); + const FlexfecReceiveStream::Config config) { + return call_->CreateFlexfecReceiveStream(std::move(config)); } void DegradedCall::DestroyFlexfecReceiveStream( @@ -278,7 +277,7 @@ Call::Stats DegradedCall::GetStats() const { return call_->GetStats(); } -const WebRtcKeyValueConfig& DegradedCall::trials() const { +const FieldTrialsView& DegradedCall::trials() const { return call_->trials(); } diff --git a/call/degraded_call.h b/call/degraded_call.h index 87a83f9730..59f5236593 100644 --- a/call/degraded_call.h +++ b/call/degraded_call.h @@ -52,8 +52,7 @@ class DegradedCall : public Call, private PacketReceiver { explicit DegradedCall( std::unique_ptr call, const std::vector& send_configs, - const std::vector& receive_configs, - TaskQueueFactory* task_queue_factory); + const std::vector& receive_configs); ~DegradedCall() override; // Implements Call. @@ -79,7 +78,7 @@ class DegradedCall : public Call, private PacketReceiver { void DestroyVideoReceiveStream(VideoReceiveStream* receive_stream) override; FlexfecReceiveStream* CreateFlexfecReceiveStream( - const FlexfecReceiveStream::Config& config) override; + const FlexfecReceiveStream::Config config) override; void DestroyFlexfecReceiveStream( FlexfecReceiveStream* receive_stream) override; @@ -91,7 +90,7 @@ class DegradedCall : public Call, private PacketReceiver { Stats GetStats() const override; - const WebRtcKeyValueConfig& trials() const override; + const FieldTrialsView& trials() const override; TaskQueueBase* network_thread() const override; TaskQueueBase* worker_thread() const override; @@ -115,7 +114,8 @@ class DegradedCall : public Call, private PacketReceiver { class FakeNetworkPipeOnTaskQueue { public: FakeNetworkPipeOnTaskQueue( - TaskQueueFactory* task_queue_factory, + TaskQueueBase* task_queue, + const ScopedTaskSafety& task_safety, Clock* clock, std::unique_ptr network_behavior); @@ -134,7 +134,8 @@ class DegradedCall : public Call, private PacketReceiver { bool Process(); Clock* const clock_; - rtc::TaskQueue task_queue_; + TaskQueueBase* const task_queue_; + const ScopedTaskSafety& task_safety_; FakeNetworkPipe pipe_; absl::optional next_process_ms_ RTC_GUARDED_BY(&task_queue_); }; @@ -171,7 +172,6 @@ class DegradedCall : public Call, private PacketReceiver { Clock* const clock_; const std::unique_ptr call_; ScopedTaskSafety task_safety_; - TaskQueueFactory* const task_queue_factory_; size_t send_config_index_; const std::vector send_configs_; SimulatedNetwork* send_simulated_network_; diff --git a/call/flexfec_receive_stream.h b/call/flexfec_receive_stream.h index 72e544e7ec..118eb0bce5 100644 --- a/call/flexfec_receive_stream.h +++ b/call/flexfec_receive_stream.h @@ -50,7 +50,7 @@ class FlexfecReceiveStream : public RtpPacketSinkInterface, // Payload type for FlexFEC. int payload_type = -1; - RtpConfig rtp; + ReceiveStreamRtpConfig rtp; // Vector containing a single element, corresponding to the SSRC of the // media stream being protected by this FlexFEC stream. The vector MUST have diff --git a/call/flexfec_receive_stream_impl.cc b/call/flexfec_receive_stream_impl.cc index a78448170f..6f2b5dcad7 100644 --- a/call/flexfec_receive_stream_impl.cc +++ b/call/flexfec_receive_stream_impl.cc @@ -137,10 +137,11 @@ std::unique_ptr CreateRtpRtcpModule( FlexfecReceiveStreamImpl::FlexfecReceiveStreamImpl( Clock* clock, - const Config& config, + Config config, RecoveredPacketReceiver* recovered_packet_receiver, RtcpRttStats* rtt_stats) - : config_(config), + : extension_map_(std::move(config.rtp.extensions)), + config_(std::move(config)), receiver_(MaybeCreateFlexfecReceiver(clock, config_, recovered_packet_receiver)), @@ -174,7 +175,7 @@ void FlexfecReceiveStreamImpl::RegisterWithTransport( // here at all, we'd then delete the OnRtpPacket method and instead register // `receiver_` as the RtpPacketSinkInterface for this stream. rtp_stream_receiver_ = - receiver_controller->CreateReceiver(config_.rtp.remote_ssrc, this); + receiver_controller->CreateReceiver(remote_ssrc(), this); } void FlexfecReceiveStreamImpl::UnregisterFromTransport() { @@ -190,7 +191,7 @@ void FlexfecReceiveStreamImpl::OnRtpPacket(const RtpPacketReceived& packet) { receiver_->OnRtpPacket(packet); // Do not report media packets in the RTCP RRs generated by `rtp_rtcp_`. - if (packet.Ssrc() == config_.rtp.remote_ssrc) { + if (packet.Ssrc() == remote_ssrc()) { rtp_receive_statistics_->OnRtpPacket(packet); } } @@ -204,7 +205,12 @@ FlexfecReceiveStreamImpl::Stats FlexfecReceiveStreamImpl::GetStats() const { void FlexfecReceiveStreamImpl::SetRtpExtensions( std::vector extensions) { RTC_DCHECK_RUN_ON(&packet_sequence_checker_); - config_.rtp.extensions = std::move(extensions); + extension_map_.Reset(extensions); +} + +RtpHeaderExtensionMap FlexfecReceiveStreamImpl::GetRtpExtensionMap() const { + RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + return extension_map_; } } // namespace webrtc diff --git a/call/flexfec_receive_stream_impl.h b/call/flexfec_receive_stream_impl.h index c2407cd419..e25b7f09c2 100644 --- a/call/flexfec_receive_stream_impl.h +++ b/call/flexfec_receive_stream_impl.h @@ -34,7 +34,7 @@ class RtpStreamReceiverInterface; class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { public: FlexfecReceiveStreamImpl(Clock* clock, - const Config& config, + Config config, RecoveredPacketReceiver* recovered_packet_receiver, RtcpRttStats* rtt_stats); // Destruction happens on the worker thread. Prior to destruction the caller @@ -60,13 +60,23 @@ class FlexfecReceiveStreamImpl : public FlexfecReceiveStream { // ReceiveStream impl. void SetRtpExtensions(std::vector extensions) override; - const RtpConfig& rtp_config() const override { return config_.rtp; } + RtpHeaderExtensionMap GetRtpExtensionMap() const override; + + uint32_t remote_ssrc() const { return config_.rtp.remote_ssrc; } + bool transport_cc() const override { + RTC_DCHECK_RUN_ON(&packet_sequence_checker_); + return config_.rtp.transport_cc; + } private: RTC_NO_UNIQUE_ADDRESS SequenceChecker packet_sequence_checker_; - // Config. Mostly const, header extensions may change. - Config config_ RTC_GUARDED_BY(packet_sequence_checker_); + RtpHeaderExtensionMap extension_map_; + + // Config. Mostly const, header extensions may change, which is an exception + // case that's specifically handled in `SetRtpExtensions`, which must be + // called on the `packet_sequence_checker` thread. + const Config config_; // Erasure code interfacing. const std::unique_ptr receiver_; diff --git a/call/rampup_tests.cc b/call/rampup_tests.cc index abd0f1f348..999bb50815 100644 --- a/call/rampup_tests.cc +++ b/call/rampup_tests.cc @@ -26,7 +26,6 @@ #include "rtc_base/task_queue_for_test.h" #include "rtc_base/time_utils.h" #include "test/encoder_settings.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/testsupport/perf_test.h" diff --git a/call/receive_stream.h b/call/receive_stream.h index a6756fc5c1..0464a70a28 100644 --- a/call/receive_stream.h +++ b/call/receive_stream.h @@ -18,6 +18,7 @@ #include "api/media_types.h" #include "api/scoped_refptr.h" #include "api/transport/rtp/rtp_source.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" namespace webrtc { @@ -26,7 +27,9 @@ namespace webrtc { class ReceiveStream { public: // Receive-stream specific RTP settings. - struct RtpConfig { + // TODO(tommi): This struct isn't needed at this level anymore. Move it closer + // to where it's used. + struct ReceiveStreamRtpConfig { // Synchronization source (stream identifier) to be received. // This member will not change mid-stream and can be assumed to be const // post initialization. @@ -54,12 +57,15 @@ class ReceiveStream { // Set/change the rtp header extensions. Must be called on the packet // delivery thread. virtual void SetRtpExtensions(std::vector extensions) = 0; - - // Called on the packet delivery thread since some members of the config may - // change mid-stream (e.g. the local ssrc). All mutation must also happen on - // the packet delivery thread. Return value can be assumed to - // only be used in the calling context (on the stack basically). - virtual const RtpConfig& rtp_config() const = 0; + virtual RtpHeaderExtensionMap GetRtpExtensionMap() const = 0; + + // Returns a bool for whether feedback for send side bandwidth estimation is + // enabled. See + // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions + // for details. + // This value may change mid-stream and must be done on the same thread + // that the value is read on (i.e. packet delivery). + virtual bool transport_cc() const = 0; protected: virtual ~ReceiveStream() {} diff --git a/call/receive_time_calculator.cc b/call/receive_time_calculator.cc index 94d1fd18cc..417168b15d 100644 --- a/call/receive_time_calculator.cc +++ b/call/receive_time_calculator.cc @@ -16,22 +16,20 @@ #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/numerics/safe_minmax.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { -using ::webrtc::field_trial::IsEnabled; const char kBweReceiveTimeCorrection[] = "WebRTC-Bwe-ReceiveTimeFix"; } // namespace -ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig() +ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig( + const FieldTrialsView& field_trials) : max_packet_time_repair("maxrep", TimeDelta::Millis(2000)), stall_threshold("stall", TimeDelta::Millis(5)), tolerance("tol", TimeDelta::Millis(1)), max_stall("maxstall", TimeDelta::Seconds(5)) { - std::string trial_string = - field_trial::FindFullName(kBweReceiveTimeCorrection); + std::string trial_string = field_trials.Lookup(kBweReceiveTimeCorrection); ParseFieldTrial( {&max_packet_time_repair, &stall_threshold, &tolerance, &max_stall}, trial_string); @@ -40,14 +38,16 @@ ReceiveTimeCalculatorConfig::ReceiveTimeCalculatorConfig( const ReceiveTimeCalculatorConfig&) = default; ReceiveTimeCalculatorConfig::~ReceiveTimeCalculatorConfig() = default; -ReceiveTimeCalculator::ReceiveTimeCalculator() - : config_(ReceiveTimeCalculatorConfig()) {} +ReceiveTimeCalculator::ReceiveTimeCalculator( + const FieldTrialsView& field_trials) + : config_(field_trials) {} std::unique_ptr -ReceiveTimeCalculator::CreateFromFieldTrial() { - if (!IsEnabled(kBweReceiveTimeCorrection)) +ReceiveTimeCalculator::CreateFromFieldTrial( + const FieldTrialsView& field_trials) { + if (!field_trials.IsEnabled(kBweReceiveTimeCorrection)) return nullptr; - return std::make_unique(); + return std::make_unique(field_trials); } int64_t ReceiveTimeCalculator::ReconcileReceiveTimes(int64_t packet_time_us, diff --git a/call/receive_time_calculator.h b/call/receive_time_calculator.h index 0bd3a82afc..57ba331844 100644 --- a/call/receive_time_calculator.h +++ b/call/receive_time_calculator.h @@ -14,13 +14,14 @@ #include +#include "api/field_trials_view.h" #include "api/units/time_delta.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { struct ReceiveTimeCalculatorConfig { - ReceiveTimeCalculatorConfig(); + explicit ReceiveTimeCalculatorConfig(const FieldTrialsView& field_trials); ReceiveTimeCalculatorConfig(const ReceiveTimeCalculatorConfig&); ReceiveTimeCalculatorConfig& operator=(const ReceiveTimeCalculatorConfig&) = default; @@ -41,8 +42,9 @@ struct ReceiveTimeCalculatorConfig { // is received. class ReceiveTimeCalculator { public: - static std::unique_ptr CreateFromFieldTrial(); - ReceiveTimeCalculator(); + static std::unique_ptr CreateFromFieldTrial( + const FieldTrialsView& field_trials); + explicit ReceiveTimeCalculator(const FieldTrialsView& field_trials); int64_t ReconcileReceiveTimes(int64_t packet_time_us_, int64_t system_time_us_, int64_t safe_time_us_); diff --git a/call/receive_time_calculator_unittest.cc b/call/receive_time_calculator_unittest.cc index d18fb1be8b..f2e3d54f0c 100644 --- a/call/receive_time_calculator_unittest.cc +++ b/call/receive_time_calculator_unittest.cc @@ -21,6 +21,7 @@ #include "rtc_base/random.h" #include "rtc_base/time_utils.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace test { @@ -168,6 +169,7 @@ class EmulatedNonMonotoneousClock : public EmulatedClock { }; TEST(ClockRepair, NoClockDrift) { + webrtc::test::ScopedKeyValueConfig field_trials; const int kSeeds = 10; const int kFirstSeed = 1; const int64_t kRuntimeUs = 10 * rtc::kNumMicrosecsPerSec; @@ -177,7 +179,7 @@ TEST(ClockRepair, NoClockDrift) { EmulatedMonotoneousClock monotone_clock(seed); EmulatedNonMonotoneousClock non_monotone_clock( seed + 1, kRuntimeUs + rtc::kNumMicrosecsPerSec, kDrift); - ReceiveTimeCalculator reception_time_tracker; + ReceiveTimeCalculator reception_time_tracker(field_trials); int64_t corrected_clock_0 = 0; int64_t reset_during_stall_tol_us = 0; bool initial_clock_stall = true; diff --git a/call/rtp_payload_params.cc b/call/rtp_payload_params.cc index 5eff91fa5c..470d96acbb 100644 --- a/call/rtp_payload_params.cc +++ b/call/rtp_payload_params.cc @@ -127,7 +127,7 @@ void SetVideoTiming(const EncodedImage& image, VideoSendTiming* timing) { RtpPayloadParams::RtpPayloadParams(const uint32_t ssrc, const RtpPayloadState* state, - const WebRtcKeyValueConfig& trials) + const FieldTrialsView& trials) : ssrc_(ssrc), generic_picture_id_experiment_( absl::StartsWith(trials.Lookup("WebRTC-GenericPictureId"), @@ -398,6 +398,15 @@ void RtpPayloadParams::Vp8ToGeneric(const CodecSpecificInfoVP8& vp8_info, generic.spatial_index = spatial_index; generic.temporal_index = temporal_index; + // Generate decode target indications. + RTC_DCHECK_LT(temporal_index, kMaxTemporalStreams); + generic.decode_target_indications.resize(kMaxTemporalStreams); + auto it = std::fill_n(generic.decode_target_indications.begin(), + temporal_index, DecodeTargetIndication::kNotPresent); + std::fill(it, generic.decode_target_indications.end(), + DecodeTargetIndication::kSwitch); + + // Frame dependencies. if (vp8_info.useExplicitDependencies) { SetDependenciesVp8New(vp8_info, shared_frame_id, is_keyframe, vp8_header.layerSync, &generic); @@ -406,6 +415,15 @@ void RtpPayloadParams::Vp8ToGeneric(const CodecSpecificInfoVP8& vp8_info, spatial_index, temporal_index, vp8_header.layerSync, &generic); } + + // Calculate chains. + generic.chain_diffs = { + (is_keyframe || chain_last_frame_id_[0] < 0) + ? 0 + : static_cast(shared_frame_id - chain_last_frame_id_[0])}; + if (temporal_index == 0) { + chain_last_frame_id_[0] = shared_frame_id; + } } FrameDependencyStructure RtpPayloadParams::MinimalisticStructure( diff --git a/call/rtp_payload_params.h b/call/rtp_payload_params.h index 1f03fab6da..ff2de731e7 100644 --- a/call/rtp_payload_params.h +++ b/call/rtp_payload_params.h @@ -15,7 +15,7 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/video_codecs/video_encoder.h" #include "call/rtp_config.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor.h" @@ -34,7 +34,7 @@ class RtpPayloadParams final { public: RtpPayloadParams(uint32_t ssrc, const RtpPayloadState* state, - const WebRtcKeyValueConfig& trials); + const FieldTrialsView& trials); RtpPayloadParams(const RtpPayloadParams& other); ~RtpPayloadParams(); diff --git a/call/rtp_payload_params_unittest.cc b/call/rtp_payload_params_unittest.cc index 8b22716f43..169a82d198 100644 --- a/call/rtp_payload_params_unittest.cc +++ b/call/rtp_payload_params_unittest.cc @@ -27,18 +27,24 @@ #include "modules/video_coding/codecs/vp9/include/vp9_globals.h" #include "modules/video_coding/include/video_codec_interface.h" #include "test/explicit_key_value_config.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" +namespace webrtc { +namespace { + +using ::testing::AllOf; using ::testing::Each; using ::testing::ElementsAre; using ::testing::Eq; +using ::testing::Field; using ::testing::IsEmpty; +using ::testing::Optional; using ::testing::SizeIs; -namespace webrtc { -namespace { +using GenericDescriptorInfo = RTPVideoHeader::GenericDescriptorInfo; + const uint32_t kSsrc1 = 12345; const uint32_t kSsrc2 = 23456; const int16_t kPictureId = 123; @@ -47,7 +53,6 @@ const uint8_t kTemporalIdx = 1; const int16_t kInitialPictureId1 = 222; const int16_t kInitialTl0PicIdx1 = 99; const int64_t kDontCare = 0; -} // namespace TEST(RtpPayloadParamsTest, InfoMappedToRtpVideoHeader_Vp8) { RtpPayloadState state2; @@ -193,6 +198,87 @@ TEST(RtpPayloadParamsTest, PictureIdWraps) { EXPECT_EQ(kInitialTl0PicIdx1, params.state().tl0_pic_idx); } +TEST(RtpPayloadParamsTest, CreatesGenericDescriptorForVp8) { + constexpr auto kSwitch = DecodeTargetIndication::kSwitch; + constexpr auto kNotPresent = DecodeTargetIndication::kNotPresent; + + RtpPayloadState state; + RtpPayloadParams params(kSsrc1, &state, FieldTrialBasedConfig()); + + EncodedImage key_frame_image; + key_frame_image._frameType = VideoFrameType::kVideoFrameKey; + CodecSpecificInfo key_frame_info; + key_frame_info.codecType = kVideoCodecVP8; + key_frame_info.codecSpecific.VP8.temporalIdx = 0; + RTPVideoHeader key_frame_header = params.GetRtpVideoHeader( + key_frame_image, &key_frame_info, /*shared_frame_id=*/123); + + EncodedImage delta_t1_image; + delta_t1_image._frameType = VideoFrameType::kVideoFrameDelta; + CodecSpecificInfo delta_t1_info; + delta_t1_info.codecType = kVideoCodecVP8; + delta_t1_info.codecSpecific.VP8.temporalIdx = 1; + RTPVideoHeader delta_t1_header = params.GetRtpVideoHeader( + delta_t1_image, &delta_t1_info, /*shared_frame_id=*/124); + + EncodedImage delta_t0_image; + delta_t0_image._frameType = VideoFrameType::kVideoFrameDelta; + CodecSpecificInfo delta_t0_info; + delta_t0_info.codecType = kVideoCodecVP8; + delta_t0_info.codecSpecific.VP8.temporalIdx = 0; + RTPVideoHeader delta_t0_header = params.GetRtpVideoHeader( + delta_t0_image, &delta_t0_info, /*shared_frame_id=*/125); + + EXPECT_THAT( + key_frame_header, + AllOf(Field(&RTPVideoHeader::codec, kVideoCodecVP8), + Field(&RTPVideoHeader::frame_type, VideoFrameType::kVideoFrameKey), + Field(&RTPVideoHeader::generic, + Optional(AllOf( + Field(&GenericDescriptorInfo::frame_id, 123), + Field(&GenericDescriptorInfo::spatial_index, 0), + Field(&GenericDescriptorInfo::temporal_index, 0), + Field(&GenericDescriptorInfo::decode_target_indications, + ElementsAre(kSwitch, kSwitch, kSwitch, kSwitch)), + Field(&GenericDescriptorInfo::dependencies, IsEmpty()), + Field(&GenericDescriptorInfo::chain_diffs, + ElementsAre(0))))))); + + EXPECT_THAT( + delta_t1_header, + AllOf( + Field(&RTPVideoHeader::codec, kVideoCodecVP8), + Field(&RTPVideoHeader::frame_type, VideoFrameType::kVideoFrameDelta), + Field( + &RTPVideoHeader::generic, + Optional(AllOf( + Field(&GenericDescriptorInfo::frame_id, 124), + Field(&GenericDescriptorInfo::spatial_index, 0), + Field(&GenericDescriptorInfo::temporal_index, 1), + Field(&GenericDescriptorInfo::decode_target_indications, + ElementsAre(kNotPresent, kSwitch, kSwitch, kSwitch)), + Field(&GenericDescriptorInfo::dependencies, ElementsAre(123)), + Field(&GenericDescriptorInfo::chain_diffs, + ElementsAre(1))))))); + + EXPECT_THAT( + delta_t0_header, + AllOf( + Field(&RTPVideoHeader::codec, kVideoCodecVP8), + Field(&RTPVideoHeader::frame_type, VideoFrameType::kVideoFrameDelta), + Field( + &RTPVideoHeader::generic, + Optional(AllOf( + Field(&GenericDescriptorInfo::frame_id, 125), + Field(&GenericDescriptorInfo::spatial_index, 0), + Field(&GenericDescriptorInfo::temporal_index, 0), + Field(&GenericDescriptorInfo::decode_target_indications, + ElementsAre(kSwitch, kSwitch, kSwitch, kSwitch)), + Field(&GenericDescriptorInfo::dependencies, ElementsAre(123)), + Field(&GenericDescriptorInfo::chain_diffs, + ElementsAre(2))))))); +} + TEST(RtpPayloadParamsTest, Tl0PicIdxUpdatedForVp8) { RtpPayloadState state; state.picture_id = kInitialPictureId1; @@ -275,8 +361,7 @@ TEST(RtpPayloadParamsTest, Tl0PicIdxUpdatedForVp9) { } TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { - test::ScopedFieldTrials generic_picture_id( - "WebRTC-GenericPictureId/Enabled/"); + test::ScopedKeyValueConfig field_trials("WebRTC-GenericPictureId/Enabled/"); RtpPayloadState state{}; EncodedImage encoded_image; @@ -284,7 +369,7 @@ TEST(RtpPayloadParamsTest, PictureIdForOldGenericFormat) { codec_info.codecType = kVideoCodecGeneric; encoded_image._frameType = VideoFrameType::kVideoFrameKey; - RtpPayloadParams params(kSsrc1, &state, FieldTrialBasedConfig()); + RtpPayloadParams params(kSsrc1, &state, field_trials); RTPVideoHeader header = params.GetRtpVideoHeader(encoded_image, &codec_info, 10); @@ -968,4 +1053,5 @@ TEST_F(RtpPayloadParamsH264ToGenericTest, FrameIdGaps) { ConvertAndCheck(1, 20, VideoFrameType::kVideoFrameDelta, kNoSync, {10, 15}); } +} // namespace } // namespace webrtc diff --git a/call/rtp_transport_config.h b/call/rtp_transport_config.h index 3a2c76b3d7..f2030b3672 100644 --- a/call/rtp_transport_config.h +++ b/call/rtp_transport_config.h @@ -13,11 +13,11 @@ #include +#include "api/field_trials_view.h" #include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/bitrate_settings.h" #include "api/transport/network_control.h" -#include "api/transport/webrtc_key_value_config.h" #include "rtc_base/task_queue.h" namespace webrtc { @@ -43,7 +43,7 @@ struct RtpTransportConfig { // Key-value mapping of internal configurations to apply, // e.g. field trials. - const WebRtcKeyValueConfig* trials = nullptr; + const FieldTrialsView* trials = nullptr; }; } // namespace webrtc diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index 230b048ce4..c5df7d7a18 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -59,14 +59,12 @@ TargetRateConstraints ConvertConstraints(const BitrateConstraints& contraints, contraints.start_bitrate_bps, clock); } -bool IsEnabled(const WebRtcKeyValueConfig* trials, absl::string_view key) { - RTC_DCHECK(trials != nullptr); - return absl::StartsWith(trials->Lookup(key), "Enabled"); +bool IsEnabled(const FieldTrialsView& trials, absl::string_view key) { + return absl::StartsWith(trials.Lookup(key), "Enabled"); } -bool IsDisabled(const WebRtcKeyValueConfig* trials, absl::string_view key) { - RTC_DCHECK(trials != nullptr); - return absl::StartsWith(trials->Lookup(key), "Disabled"); +bool IsDisabled(const FieldTrialsView& trials, absl::string_view key) { + return absl::StartsWith(trials.Lookup(key), "Disabled"); } bool IsRelayed(const rtc::NetworkRoute& route) { @@ -76,12 +74,12 @@ bool IsRelayed(const rtc::NetworkRoute& route) { } // namespace RtpTransportControllerSend::PacerSettings::PacerSettings( - const WebRtcKeyValueConfig* trials) + const FieldTrialsView& trials) : tq_disabled("Disabled"), - holdback_window("holdback_window", PacingController::kMinSleepTime), - holdback_packets("holdback_packets", -1) { + holdback_window("holdback_window", TimeDelta::Millis(5)), + holdback_packets("holdback_packets", 3) { ParseFieldTrial({&tq_disabled, &holdback_window, &holdback_packets}, - trials->Lookup("WebRTC-TaskQueuePacer")); + trials.Lookup("WebRTC-TaskQueuePacer")); } RtpTransportControllerSend::RtpTransportControllerSend( @@ -92,7 +90,7 @@ RtpTransportControllerSend::RtpTransportControllerSend( const BitrateConstraints& bitrate_config, std::unique_ptr process_thread, TaskQueueFactory* task_queue_factory, - const WebRtcKeyValueConfig* trials) + const FieldTrialsView& trials) : clock_(clock), event_log_(event_log), bitrate_configurator_(bitrate_config), @@ -103,14 +101,12 @@ RtpTransportControllerSend::RtpTransportControllerSend( ? nullptr : new PacedSender(clock, &packet_router_, - event_log, trials, process_thread_.get())), task_queue_pacer_( pacer_settings_.use_task_queue_pacer() ? new TaskQueuePacedSender(clock, &packet_router_, - event_log, trials, task_queue_factory, pacer_settings_.holdback_window.Get(), @@ -131,21 +127,24 @@ RtpTransportControllerSend::RtpTransportControllerSend( relay_bandwidth_cap_("relay_cap", DataRate::PlusInfinity()), transport_overhead_bytes_per_packet_(0), network_available_(false), + congestion_window_size_(DataSize::PlusInfinity()), + is_congested_(false), retransmission_rate_limiter_(clock, kRetransmitWindowSizeMs), task_queue_(task_queue_factory->CreateTaskQueue( "rtp_send_controller", - TaskQueueFactory::Priority::NORMAL)) { + TaskQueueFactory::Priority::NORMAL)), + field_trials_(trials) { ParseFieldTrial({&relay_bandwidth_cap_}, - trials->Lookup("WebRTC-Bwe-NetworkRouteConstraints")); + trials.Lookup("WebRTC-Bwe-NetworkRouteConstraints")); initial_config_.constraints = ConvertConstraints(bitrate_config, clock_); initial_config_.event_log = event_log; - initial_config_.key_value_config = trials; + initial_config_.key_value_config = &trials; RTC_DCHECK(bitrate_config.start_bitrate_bps > 0); pacer()->SetPacingRates( DataRate::BitsPerSec(bitrate_config.start_bitrate_bps), DataRate::Zero()); - if (absl::StartsWith(trials->Lookup("WebRTC-LazyPacerStart"), "Disabled")) { + if (absl::StartsWith(trials.Lookup("WebRTC-LazyPacerStart"), "Disabled")) { EnsureStarted(); } } @@ -174,7 +173,8 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( // the parts of RtpTransportControllerSendInterface that are really used. this, event_log, &retransmission_rate_limiter_, std::move(fec_controller), frame_encryption_config.frame_encryptor, - frame_encryption_config.crypto_options, std::move(frame_transformer))); + frame_encryption_config.crypto_options, std::move(frame_transformer), + field_trials_)); return video_rtp_senders_.back().get(); } @@ -202,6 +202,15 @@ void RtpTransportControllerSend::UpdateControlState() { observer_->OnTargetTransferRate(*update); } +void RtpTransportControllerSend::UpdateCongestedState() { + bool congested = transport_feedback_adapter_.GetOutstandingData() >= + congestion_window_size_; + if (congested != is_congested_) { + is_congested_ = congested; + pacer()->SetCongested(congested); + } +} + RtpPacketPacer* RtpTransportControllerSend::pacer() { if (pacer_settings_.use_task_queue_pacer()) { return task_queue_pacer_.get(); @@ -361,7 +370,8 @@ void RtpTransportControllerSend::OnNetworkRouteChanged( } else { UpdateInitialConstraints(msg.constraints); } - pacer()->UpdateOutstandingData(DataSize::Zero()); + is_congested_ = false; + pacer()->SetCongested(false); }); } } @@ -382,7 +392,8 @@ void RtpTransportControllerSend::OnNetworkAvailability(bool network_available) { } else { pacer()->Pause(); } - pacer()->UpdateOutstandingData(DataSize::Zero()); + is_congested_ = false; + pacer()->SetCongested(false); if (controller_) { control_handler_->SetNetworkAvailability(network_available_); @@ -421,12 +432,11 @@ void RtpTransportControllerSend::OnSentPacket( absl::optional packet_msg = transport_feedback_adapter_.ProcessSentPacket(sent_packet); if (packet_msg) { - // Only update outstanding data in pacer if: + // Only update outstanding data if: // 1. Packet feadback is used. // 2. The packet has not yet received an acknowledgement. // 3. It is not a retransmission of an earlier packet. - pacer()->UpdateOutstandingData( - transport_feedback_adapter_.GetOutstandingData()); + UpdateCongestedState(); if (controller_) PostUpdates(controller_->OnSentPacket(*packet_msg)); } @@ -583,10 +593,8 @@ void RtpTransportControllerSend::OnTransportFeedback( if (controller_) PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg)); - // Only update outstanding data in pacer if any packet is first time - // acked. - pacer()->UpdateOutstandingData( - transport_feedback_adapter_.GetOutstandingData()); + // Only update outstanding data if any packet is first time acked. + UpdateCongestedState(); } }); } @@ -678,7 +686,8 @@ void RtpTransportControllerSend::UpdateStreamsConfig() { void RtpTransportControllerSend::PostUpdates(NetworkControlUpdate update) { if (update.congestion_window) { - pacer()->SetCongestionWindow(*update.congestion_window); + congestion_window_size_ = *update.congestion_window; + UpdateCongestedState(); } if (update.pacer_config) { pacer()->SetPacingRates(update.pacer_config->data_rate(), diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index e5ff162687..d9461db98a 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -59,7 +59,7 @@ class RtpTransportControllerSend final const BitrateConstraints& bitrate_config, std::unique_ptr process_thread, TaskQueueFactory* task_queue_factory, - const WebRtcKeyValueConfig* trials); + const FieldTrialsView& trials); ~RtpTransportControllerSend() override; RtpTransportControllerSend(const RtpTransportControllerSend&) = delete; @@ -132,7 +132,7 @@ class RtpTransportControllerSend final private: struct PacerSettings { - explicit PacerSettings(const WebRtcKeyValueConfig* trials); + explicit PacerSettings(const FieldTrialsView& trials); bool use_task_queue_pacer() const { return !tq_disabled.Get(); } @@ -158,6 +158,7 @@ class RtpTransportControllerSend final RTC_RUN_ON(task_queue_); void PostUpdates(NetworkControlUpdate update) RTC_RUN_ON(task_queue_); void UpdateControlState() RTC_RUN_ON(task_queue_); + void UpdateCongestedState() RTC_RUN_ON(task_queue_); RtpPacketPacer* pacer(); const RtpPacketPacer* pacer() const; @@ -211,6 +212,9 @@ class RtpTransportControllerSend final RepeatingTaskHandle pacer_queue_update_task_ RTC_GUARDED_BY(task_queue_); RepeatingTaskHandle controller_task_ RTC_GUARDED_BY(task_queue_); + DataSize congestion_window_size_ RTC_GUARDED_BY(task_queue_); + bool is_congested_ RTC_GUARDED_BY(task_queue_); + // Protected by internal locks. RateLimiter retransmission_rate_limiter_; @@ -218,6 +222,8 @@ class RtpTransportControllerSend final // `task_queue_` is defined last to ensure all pending tasks are cancelled // and deleted before any other members. rtc::TaskQueue task_queue_; + + const FieldTrialsView& field_trials_; }; } // namespace webrtc diff --git a/call/rtp_transport_controller_send_factory.h b/call/rtp_transport_controller_send_factory.h index a857ca7e6f..bda0be04af 100644 --- a/call/rtp_transport_controller_send_factory.h +++ b/call/rtp_transport_controller_send_factory.h @@ -25,10 +25,11 @@ class RtpTransportControllerSendFactory const RtpTransportConfig& config, Clock* clock, std::unique_ptr process_thread) override { + RTC_CHECK(config.trials); return std::make_unique( clock, config.event_log, config.network_state_predictor_factory, config.network_controller_factory, config.bitrate_config, - std::move(process_thread), config.task_queue_factory, config.trials); + std::move(process_thread), config.task_queue_factory, *config.trials); } virtual ~RtpTransportControllerSendFactory() {} diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index 35e6beeb7c..2db283d7f8 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -56,7 +56,7 @@ static const size_t kPathMTU = 1500; using webrtc_internal_rtp_video_sender::RtpStreamSender; bool PayloadTypeSupportsSkippingFecPackets(const std::string& payload_name, - const WebRtcKeyValueConfig& trials) { + const FieldTrialsView& trials) { const VideoCodecType codecType = PayloadStringToCodecType(payload_name); if (codecType == kVideoCodecVP8 || codecType == kVideoCodecVP9) { return true; @@ -70,7 +70,7 @@ bool PayloadTypeSupportsSkippingFecPackets(const std::string& payload_name, bool ShouldDisableRedAndUlpfec(bool flexfec_enabled, const RtpConfig& rtp_config, - const WebRtcKeyValueConfig& trials) { + const FieldTrialsView& trials) { // Consistency of NACK and RED+ULPFEC parameters is checked in this function. const bool nack_enabled = rtp_config.nack.rtp_history_ms > 0; @@ -126,7 +126,7 @@ std::unique_ptr MaybeCreateFecGenerator( const RtpConfig& rtp, const std::map& suspended_ssrcs, int simulcast_index, - const WebRtcKeyValueConfig& trials) { + const FieldTrialsView& trials) { // If flexfec is configured that takes priority. if (rtp.flexfec.payload_type >= 0) { RTC_DCHECK_GE(rtp.flexfec.payload_type, 0); @@ -197,7 +197,7 @@ std::vector CreateRtpStreamSenders( FrameEncryptorInterface* frame_encryptor, const CryptoOptions& crypto_options, rtc::scoped_refptr frame_transformer, - const WebRtcKeyValueConfig& trials) { + const FieldTrialsView& trials) { RTC_DCHECK_GT(rtp_config.ssrcs.size(), 0); RtpRtcpInterface::Configuration configuration; @@ -358,8 +358,10 @@ RtpVideoSender::RtpVideoSender( std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, const CryptoOptions& crypto_options, - rtc::scoped_refptr frame_transformer) - : send_side_bwe_with_overhead_(!absl::StartsWith( + rtc::scoped_refptr frame_transformer, + const FieldTrialsView& field_trials) + : field_trials_(field_trials), + send_side_bwe_with_overhead_(!absl::StartsWith( field_trials_.Lookup("WebRTC-SendSideBwe-WithOverhead"), "Disabled")), use_frame_rate_for_overhead_(absl::StartsWith( @@ -581,12 +583,23 @@ EncodedImageCallback::Result RtpVideoSender::OnEncodedImage( } if (IsFirstFrameOfACodedVideoSequence(encoded_image, codec_specific_info)) { - // If encoder adapter produce FrameDependencyStructure, pass it so that - // dependency descriptor rtp header extension can be used. - // If not supported, disable using dependency descriptor by passing nullptr. + // In order to use the dependency descriptor RTP header extension: + // - Pass along any `FrameDependencyStructure` templates produced by the + // encoder adapter. + // - If none were produced the `RtpPayloadParams::*ToGeneric` for the + // particular codec have simulated a dependency structure, so provide a + // minimal set of templates. + // - Otherwise, don't pass along any templates at all which will disable + // the generation of a dependency descriptor. RTPSenderVideo& sender_video = *rtp_streams_[stream_index].sender_video; if (codec_specific_info && codec_specific_info->template_structure) { sender_video.SetVideoStructure(&*codec_specific_info->template_structure); + } else if (codec_specific_info && + codec_specific_info->codecType == kVideoCodecVP8) { + FrameDependencyStructure structure = + RtpPayloadParams::MinimalisticStructure(/*num_spatial_layers=*/1, + kMaxTemporalStreams); + sender_video.SetVideoStructure(&structure); } else if (codec_specific_info && codec_specific_info->codecType == kVideoCodecVP9) { const CodecSpecificInfoVP9& vp9 = codec_specific_info->codecSpecific.VP9; diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index 7023804506..c4a2b92011 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -21,9 +21,9 @@ #include "api/call/transport.h" #include "api/fec_controller.h" #include "api/fec_controller_override.h" +#include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/sequence_checker.h" -#include "api/transport/field_trial_based_config.h" #include "api/video_codecs/video_encoder.h" #include "call/rtp_config.h" #include "call/rtp_payload_params.h" @@ -85,7 +85,8 @@ class RtpVideoSender : public RtpVideoSenderInterface, std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, const CryptoOptions& crypto_options, // move inside RtpTransport - rtc::scoped_refptr frame_transformer); + rtc::scoped_refptr frame_transformer, + const FieldTrialsView& field_trials); ~RtpVideoSender() override; RtpVideoSender(const RtpVideoSender&) = delete; @@ -166,7 +167,7 @@ class RtpVideoSender : public RtpVideoSenderInterface, DataSize overhead_per_packet, Frequency framerate) const; - const FieldTrialBasedConfig field_trials_; + const FieldTrialsView& field_trials_; const bool send_side_bwe_with_overhead_; const bool use_frame_rate_for_overhead_; const bool has_packet_feedback_; diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index c47717da7f..d3c5332356 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -24,23 +24,24 @@ #include "modules/video_coding/fec_controller_default.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/rate_limiter.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/mock_frame_transformer.h" #include "test/mock_transport.h" #include "test/scenario/scenario.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" #include "video/send_delay_stats.h" #include "video/send_statistics_proxy.h" +namespace webrtc { +namespace { + using ::testing::_; using ::testing::NiceMock; using ::testing::SaveArg; using ::testing::SizeIs; -namespace webrtc { -namespace { const int8_t kPayloadType = 96; const uint32_t kSsrc1 = 12345; const uint32_t kSsrc2 = 23456; @@ -118,7 +119,8 @@ class RtpVideoSenderTestFixture { int payload_type, const std::map& suspended_payload_states, FrameCountObserver* frame_count_observer, - rtc::scoped_refptr frame_transformer) + rtc::scoped_refptr frame_transformer, + const FieldTrialsView* field_trials = nullptr) : time_controller_(Timestamp::Millis(1000000)), config_(CreateVideoSendStreamConfig(&transport_, ssrcs, @@ -134,10 +136,11 @@ class RtpVideoSenderTestFixture { bitrate_config_, time_controller_.CreateProcessThread("PacerThread"), time_controller_.GetTaskQueueFactory(), - &field_trials_), + field_trials ? *field_trials : field_trials_), stats_proxy_(time_controller_.GetClock(), config_, - VideoEncoderConfig::ContentType::kRealtimeVideo), + VideoEncoderConfig::ContentType::kRealtimeVideo, + field_trials ? *field_trials : field_trials_), retransmission_rate_limiter_(time_controller_.GetClock(), kRetransmitWindowSizeMs) { transport_controller_.EnsureStarted(); @@ -150,7 +153,8 @@ class RtpVideoSenderTestFixture { &stats_proxy_, &stats_proxy_, &send_delay_stats_), &transport_controller_, &event_log_, &retransmission_rate_limiter_, std::make_unique(time_controller_.GetClock()), - nullptr, CryptoOptions{}, frame_transformer); + nullptr, CryptoOptions{}, frame_transformer, + field_trials ? *field_trials : field_trials_); } RtpVideoSenderTestFixture( @@ -158,25 +162,29 @@ class RtpVideoSenderTestFixture { const std::vector& rtx_ssrcs, int payload_type, const std::map& suspended_payload_states, - FrameCountObserver* frame_count_observer) + FrameCountObserver* frame_count_observer, + const FieldTrialsView* field_trials = nullptr) : RtpVideoSenderTestFixture(ssrcs, rtx_ssrcs, payload_type, suspended_payload_states, frame_count_observer, - /*frame_transformer=*/nullptr) {} + /*frame_transformer=*/nullptr, + field_trials) {} RtpVideoSenderTestFixture( const std::vector& ssrcs, const std::vector& rtx_ssrcs, int payload_type, - const std::map& suspended_payload_states) + const std::map& suspended_payload_states, + const FieldTrialsView* field_trials = nullptr) : RtpVideoSenderTestFixture(ssrcs, rtx_ssrcs, payload_type, suspended_payload_states, /*frame_count_observer=*/nullptr, - /*frame_transformer=*/nullptr) {} + /*frame_transformer=*/nullptr, + field_trials) {} ~RtpVideoSenderTestFixture() { SetActive(false); } @@ -206,6 +214,7 @@ class RtpVideoSenderTestFixture { } private: + test::ScopedKeyValueConfig field_trials_; NiceMock transport_; NiceMock encoder_feedback_; GlobalSimulatedTimeController time_controller_; @@ -213,7 +222,6 @@ class RtpVideoSenderTestFixture { VideoSendStream::Config config_; SendDelayStats send_delay_stats_; BitrateConstraints bitrate_config_; - const FieldTrialBasedConfig field_trials_; RtpTransportControllerSend transport_controller_; SendStatisticsProxy stats_proxy_; RateLimiter retransmission_rate_limiter_; @@ -739,6 +747,53 @@ TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { sent_packets.back().HasExtension()); } +TEST(RtpVideoSenderTest, + SupportsDependencyDescriptorForVp8NotProvidedByEncoder) { + constexpr uint8_t kPayload[1] = {'a'}; + RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); + RtpHeaderExtensionMap extensions; + extensions.Register( + kDependencyDescriptorExtensionId); + std::vector sent_packets; + ON_CALL(test.transport(), SendRtp) + .WillByDefault( + [&](const uint8_t* packet, size_t length, const PacketOptions&) { + EXPECT_TRUE( + sent_packets.emplace_back(&extensions).Parse(packet, length)); + return true; + }); + test.SetActive(true); + + EncodedImage key_frame_image; + key_frame_image._frameType = VideoFrameType::kVideoFrameKey; + key_frame_image.SetEncodedData( + EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); + CodecSpecificInfo key_frame_info; + key_frame_info.codecType = VideoCodecType::kVideoCodecVP8; + ASSERT_EQ( + test.router()->OnEncodedImage(key_frame_image, &key_frame_info).error, + EncodedImageCallback::Result::OK); + + EncodedImage delta_image; + delta_image._frameType = VideoFrameType::kVideoFrameDelta; + delta_image.SetEncodedData( + EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); + CodecSpecificInfo delta_info; + delta_info.codecType = VideoCodecType::kVideoCodecVP8; + ASSERT_EQ(test.router()->OnEncodedImage(delta_image, &delta_info).error, + EncodedImageCallback::Result::OK); + + test.AdvanceTime(TimeDelta::Millis(123)); + + DependencyDescriptor key_frame_dd; + DependencyDescriptor delta_dd; + ASSERT_THAT(sent_packets, SizeIs(2)); + EXPECT_TRUE(sent_packets[0].GetExtension( + /*structure=*/nullptr, &key_frame_dd)); + EXPECT_TRUE(sent_packets[1].GetExtension( + key_frame_dd.attached_structure.get(), &delta_dd)); +} + TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); test.SetActive(true); @@ -849,9 +904,9 @@ TEST(RtpVideoSenderTest, } TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { - test::ScopedFieldTrials field_trials( + test::ScopedKeyValueConfig field_trials( "WebRTC-GenericCodecDependencyDescriptor/Enabled/"); - RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); + RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); test.SetActive(true); RtpHeaderExtensionMap extensions; @@ -953,64 +1008,6 @@ TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { sent_packets.back().HasExtension()); } -TEST(RtpVideoSenderTest, - SupportsStoppingUsingDependencyDescriptorForVp8Simulcast) { - RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {}, kPayloadType, {}); - test.SetActive(true); - - RtpHeaderExtensionMap extensions; - extensions.Register( - kDependencyDescriptorExtensionId); - std::vector sent_packets; - ON_CALL(test.transport(), SendRtp) - .WillByDefault([&](const uint8_t* packet, size_t length, - const PacketOptions& options) { - sent_packets.emplace_back(&extensions); - EXPECT_TRUE(sent_packets.back().Parse(packet, length)); - return true; - }); - - const uint8_t kPayload[1] = {'a'}; - EncodedImage encoded_image; - encoded_image.SetTimestamp(1); - encoded_image.capture_time_ms_ = 2; - encoded_image.SetEncodedData( - EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); - // VP8 simulcast uses spatial index to communicate simulcast stream. - encoded_image.SetSpatialIndex(1); - - CodecSpecificInfo codec_specific; - codec_specific.codecType = VideoCodecType::kVideoCodecVP8; - codec_specific.template_structure.emplace(); - codec_specific.template_structure->num_decode_targets = 1; - codec_specific.template_structure->templates = { - FrameDependencyTemplate().T(0).Dtis("S")}; - - // Send two tiny images, mapping to single RTP packets. - // Send in a key frame. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; - codec_specific.generic_frame_info = - GenericFrameInfo::Builder().T(0).Dtis("S").Build(); - codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; - EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, - EncodedImageCallback::Result::OK); - test.AdvanceTime(TimeDelta::Millis(33)); - ASSERT_THAT(sent_packets, SizeIs(1)); - EXPECT_TRUE( - sent_packets.back().HasExtension()); - - // Send in a new key frame without the support for the dependency descriptor. - encoded_image._frameType = VideoFrameType::kVideoFrameKey; - codec_specific.template_structure = absl::nullopt; - codec_specific.generic_frame_info = absl::nullopt; - EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, - EncodedImageCallback::Result::OK); - test.AdvanceTime(TimeDelta::Millis(33)); - ASSERT_THAT(sent_packets, SizeIs(2)); - EXPECT_FALSE( - sent_packets.back().HasExtension()); -} - TEST(RtpVideoSenderTest, CanSetZeroBitrate) { RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}); test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(0), @@ -1031,7 +1028,7 @@ TEST(RtpVideoSenderTest, SimulcastSenderRegistersFrameTransformers) { } TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) { - test::ScopedFieldTrials field_trials( + test::ScopedKeyValueConfig field_trials( "WebRTC-Video-UseFrameRateForOverhead/Enabled/"); // TODO(jakobi): RTP header size should not be hard coded. @@ -1039,7 +1036,7 @@ TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) { constexpr uint32_t kTransportPacketOverheadBytes = 40; constexpr uint32_t kOverheadPerPacketBytes = kRtpHeaderSizeBytes + kTransportPacketOverheadBytes; - RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); + RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); test.router()->OnTransportOverheadChanged(kTransportPacketOverheadBytes); test.SetActive(true); diff --git a/call/version.cc b/call/version.cc index b0c5059680..3ec3264307 100644 --- a/call/version.cc +++ b/call/version.cc @@ -13,7 +13,7 @@ namespace webrtc { // The timestamp is always in UTC. -const char* const kSourceTimestamp = "WebRTC source stamp 2022-02-08T04:04:22"; +const char* const kSourceTimestamp = "WebRTC source stamp 2022-05-12T04:02:41"; void LoadWebRTCVersionInRegister() { // Using volatile to instruct the compiler to not optimize `p` away even diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index 614d5dba76..357b4e58a9 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -106,6 +106,8 @@ class VideoReceiveStream : public MediaReceiveStream { uint32_t frames_decoded = 0; // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime uint64_t total_decode_time_ms = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay + webrtc::TimeDelta total_processing_delay = webrtc::TimeDelta::Millis(0); // Total inter frame delay in seconds. // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalinterframedelay double total_inter_frame_delay = 0; @@ -172,7 +174,7 @@ class VideoReceiveStream : public MediaReceiveStream { VideoDecoderFactory* decoder_factory = nullptr; // Receive-stream specific RTP settings. - struct Rtp : public RtpConfig { + struct Rtp : public ReceiveStreamRtpConfig { Rtp(); Rtp(const Rtp&); ~Rtp(); diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn index 5b1e581410..2ae6d32710 100644 --- a/common_audio/BUILD.gn +++ b/common_audio/BUILD.gn @@ -48,8 +48,10 @@ rtc_library("common_audio") { "../api:array_view", "../rtc_base:checks", "../rtc_base:gtest_prod", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:safe_conversions", "../rtc_base:sanitizer", + "../rtc_base:timeutils", "../rtc_base/memory:aligned_malloc", "../rtc_base/system:arch", "../rtc_base/system:file_wrapper", @@ -180,7 +182,6 @@ rtc_library("common_audio_c") { ":common_audio_cc", "../rtc_base:checks", "../rtc_base:compile_assert_c", - "../rtc_base:rtc_base_approved", "../rtc_base:sanitizer", "../rtc_base/system:arch", "../system_wrappers", @@ -196,7 +197,7 @@ rtc_library("common_audio_cc") { ] deps = [ - "../rtc_base:rtc_base_approved", + "../rtc_base:safe_conversions", "../system_wrappers", ] } @@ -205,7 +206,6 @@ rtc_source_set("sinc_resampler") { sources = [ "resampler/sinc_resampler.h" ] deps = [ "../rtc_base:gtest_prod", - "../rtc_base:rtc_base_approved", "../rtc_base/memory:aligned_malloc", "../rtc_base/system:arch", "../system_wrappers", @@ -228,7 +228,6 @@ rtc_library("fir_filter_factory") { deps = [ ":fir_filter", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base/system:arch", "../system_wrappers", ] @@ -257,7 +256,6 @@ if (current_cpu == "x86" || current_cpu == "x64") { ":fir_filter", ":sinc_resampler", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base/memory:aligned_malloc", ] } @@ -282,7 +280,6 @@ if (current_cpu == "x86" || current_cpu == "x64") { ":fir_filter", ":sinc_resampler", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base/memory:aligned_malloc", ] } @@ -307,7 +304,6 @@ if (rtc_build_with_neon) { ":fir_filter", ":sinc_resampler", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base/memory:aligned_malloc", ] } @@ -329,7 +325,6 @@ if (rtc_build_with_neon) { deps = [ ":common_audio_c", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base/system:arch", ] } @@ -378,8 +373,10 @@ if (rtc_include_tests && !build_with_chromium) { ":fir_filter_factory", ":sinc_resampler", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:stringutils", + "../rtc_base:timeutils", "../rtc_base/system:arch", "../system_wrappers", "../test:fileutils", diff --git a/common_audio/audio_converter_unittest.cc b/common_audio/audio_converter_unittest.cc index 018e7b8d2e..97937c8123 100644 --- a/common_audio/audio_converter_unittest.cc +++ b/common_audio/audio_converter_unittest.cc @@ -18,7 +18,6 @@ #include "common_audio/channel_buffer.h" #include "common_audio/resampler/push_sinc_resampler.h" #include "rtc_base/arraysize.h" -#include "rtc_base/format_macros.h" #include "test/gtest.h" namespace webrtc { @@ -79,7 +78,7 @@ float ComputeSNR(const ChannelBuffer& ref, best_delay = delay; } } - printf("SNR=%.1f dB at delay=%" RTC_PRIuS "\n", best_snr, best_delay); + printf("SNR=%.1f dB at delay=%zu\n", best_snr, best_delay); return best_snr; } @@ -131,8 +130,8 @@ void RunAudioConverterTest(size_t src_channels, PushSincResampler::AlgorithmicDelaySeconds(src_sample_rate_hz) * dst_sample_rate_hz); // SNR reported on the same line later. - printf("(%" RTC_PRIuS ", %d Hz) -> (%" RTC_PRIuS ", %d Hz) ", src_channels, - src_sample_rate_hz, dst_channels, dst_sample_rate_hz); + printf("(%zu, %d Hz) -> (%zu, %d Hz) ", src_channels, src_sample_rate_hz, + dst_channels, dst_sample_rate_hz); std::unique_ptr converter = AudioConverter::Create( src_channels, src_frames, dst_channels, dst_frames); diff --git a/common_audio/vad/webrtc_vad.c b/common_audio/vad/webrtc_vad.c index 49e7682780..6dd14d8b55 100644 --- a/common_audio/vad/webrtc_vad.c +++ b/common_audio/vad/webrtc_vad.c @@ -21,7 +21,7 @@ static const int kValidRates[] = { 8000, 16000, 32000, 48000 }; static const size_t kRatesSize = sizeof(kValidRates) / sizeof(*kValidRates); static const int kMaxFrameLengthMs = 30; -VadInst* WebRtcVad_Create() { +VadInst* WebRtcVad_Create(void) { VadInstT* self = (VadInstT*)malloc(sizeof(VadInstT)); self->init_flag = 0; diff --git a/common_audio/wav_header.cc b/common_audio/wav_header.cc index 65d8be5b89..bca209a665 100644 --- a/common_audio/wav_header.cc +++ b/common_audio/wav_header.cc @@ -80,8 +80,6 @@ const uint32_t kFmtIeeeFloatSubchunkSize = // read audio samples. #pragma pack(2) struct WavHeaderPcm { - WavHeaderPcm(const WavHeaderPcm&) = default; - WavHeaderPcm& operator=(const WavHeaderPcm&) = default; RiffHeader riff; FmtPcmSubchunk fmt; struct { @@ -95,8 +93,6 @@ static_assert(sizeof(WavHeaderPcm) == kPcmWavHeaderSize, // WAV implementation. #pragma pack(2) struct WavHeaderIeeeFloat { - WavHeaderIeeeFloat(const WavHeaderIeeeFloat&) = default; - WavHeaderIeeeFloat& operator=(const WavHeaderIeeeFloat&) = default; RiffHeader riff; FmtIeeeFloatSubchunk fmt; struct { diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index 30eddc6bf6..3598afdcb6 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -56,10 +56,19 @@ rtc_library("common_video") { "../api/video_codecs:bitstream_parser_api", "../api/video_codecs:video_codecs_api", "../rtc_base", + "../rtc_base:bit_buffer", "../rtc_base:bitstream_reader", + "../rtc_base:buffer", "../rtc_base:checks", + "../rtc_base:event_tracer", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:race_checker", + "../rtc_base:rate_statistics", + "../rtc_base:refcount", "../rtc_base:rtc_task_queue", "../rtc_base:safe_minmax", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/system:rtc_export", "../system_wrappers:metrics", @@ -113,9 +122,13 @@ if (rtc_include_tests && !build_with_chromium) { "../api/video:video_rtp_headers", "../api/video_codecs:video_codecs_api", "../rtc_base", + "../rtc_base:bit_buffer", + "../rtc_base:buffer", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:timeutils", "../system_wrappers:system_wrappers", "../test:fileutils", "../test:frame_utils", diff --git a/common_video/include/video_frame_buffer.h b/common_video/include/video_frame_buffer.h index 593464abe4..22b1148df9 100644 --- a/common_video/include/video_frame_buffer.h +++ b/common_video/include/video_frame_buffer.h @@ -31,6 +31,17 @@ rtc::scoped_refptr WrapI420Buffer( int v_stride, std::function no_longer_used); +rtc::scoped_refptr WrapI422Buffer( + int width, + int height, + const uint8_t* y_plane, + int y_stride, + const uint8_t* u_plane, + int u_stride, + const uint8_t* v_plane, + int v_stride, + std::function no_longer_used); + rtc::scoped_refptr WrapI444Buffer( int width, int height, diff --git a/common_video/include/video_frame_buffer_pool.h b/common_video/include/video_frame_buffer_pool.h index f26a9f7be7..80bf9e33f3 100644 --- a/common_video/include/video_frame_buffer_pool.h +++ b/common_video/include/video_frame_buffer_pool.h @@ -17,6 +17,7 @@ #include "api/scoped_refptr.h" #include "api/video/i420_buffer.h" +#include "api/video/i422_buffer.h" #include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/race_checker.h" @@ -44,6 +45,7 @@ class VideoFrameBufferPool { // and there are less than `max_number_of_buffers` pending, a buffer is // created. Returns null otherwise. rtc::scoped_refptr CreateI420Buffer(int width, int height); + rtc::scoped_refptr CreateI422Buffer(int width, int height); rtc::scoped_refptr CreateI444Buffer(int width, int height); rtc::scoped_refptr CreateNV12Buffer(int width, int height); diff --git a/common_video/libyuv/include/webrtc_libyuv.h b/common_video/libyuv/include/webrtc_libyuv.h index 905219b6a6..d7939dcb2f 100644 --- a/common_video/libyuv/include/webrtc_libyuv.h +++ b/common_video/libyuv/include/webrtc_libyuv.h @@ -87,11 +87,24 @@ double I420SSE(const I420BufferInterface& ref_buffer, const I420BufferInterface& test_buffer); // Compute PSNR for an I420 frame (all planes). -// Returns the PSNR in decibel, to a maximum of kInfinitePSNR. +// Returns the PSNR in decibel, to a maximum of kPerfectPSNR. double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame); double I420PSNR(const I420BufferInterface& ref_buffer, const I420BufferInterface& test_buffer); +// Computes the weighted PSNR-YUV for an I420 buffer. +// +// For the definition and motivation, see +// J. Ohm, G. J. Sullivan, H. Schwarz, T. K. Tan and T. Wiegand, +// "Comparison of the Coding Efficiency of Video Coding Standards—Including +// High Efficiency Video Coding (HEVC)," in IEEE Transactions on Circuits and +// Systems for Video Technology, vol. 22, no. 12, pp. 1669-1684, Dec. 2012 +// doi: 10.1109/TCSVT.2012.2221192. +// +// Returns the PSNR-YUV in decibel, to a maximum of kPerfectPSNR. +double I420WeightedPSNR(const I420BufferInterface& ref_buffer, + const I420BufferInterface& test_buffer); + // Compute SSIM for an I420 frame (all planes). double I420SSIM(const VideoFrame* ref_frame, const VideoFrame* test_frame); double I420SSIM(const I420BufferInterface& ref_buffer, diff --git a/common_video/libyuv/libyuv_unittest.cc b/common_video/libyuv/libyuv_unittest.cc index 62d9e87fa6..f9c82f6284 100644 --- a/common_video/libyuv/libyuv_unittest.cc +++ b/common_video/libyuv/libyuv_unittest.cc @@ -114,10 +114,6 @@ void TestLibYuv::TearDown() { source_file_ = NULL; } -TEST_F(TestLibYuv, ConvertSanityTest) { - // TODO(mikhal) -} - TEST_F(TestLibYuv, ConvertTest) { // Reading YUV frame - testing on the first frame of the foreman sequence int j = 0; @@ -368,4 +364,23 @@ TEST_F(TestLibYuv, NV12Scale4x4to2x2) { ::testing::ElementsAre(Average(0, 2, 4, 6), Average(1, 3, 5, 7))); } +TEST(I420WeightedPSNRTest, SmokeTest) { + uint8_t ref_y[] = {0, 0, 0, 0}; + uint8_t ref_uv[] = {0}; + rtc::scoped_refptr ref_buffer = + I420Buffer::Copy(/*width=*/2, /*height=*/2, ref_y, /*stride_y=*/2, ref_uv, + /*stride_u=*/1, ref_uv, /*stride_v=*/1); + + uint8_t test_y[] = {1, 1, 1, 1}; + uint8_t test_uv[] = {2}; + rtc::scoped_refptr test_buffer = I420Buffer::Copy( + /*width=*/2, /*height=*/2, test_y, /*stride_y=*/2, test_uv, + /*stride_u=*/1, test_uv, /*stride_v=*/1); + + auto psnr = [](double mse) { return 10.0 * log10(255.0 * 255.0 / mse); }; + EXPECT_NEAR(I420WeightedPSNR(*ref_buffer, *test_buffer), + (6.0 * psnr(1.0) + psnr(4.0) + psnr(4.0)) / 8.0, + /*abs_error=*/0.001); +} + } // namespace webrtc diff --git a/common_video/libyuv/webrtc_libyuv.cc b/common_video/libyuv/webrtc_libyuv.cc index 2e10a60776..51a766c1be 100644 --- a/common_video/libyuv/webrtc_libyuv.cc +++ b/common_video/libyuv/webrtc_libyuv.cc @@ -255,6 +255,45 @@ double I420PSNR(const VideoFrame* ref_frame, const VideoFrame* test_frame) { *test_frame->video_frame_buffer()->ToI420()); } +double I420WeightedPSNR(const I420BufferInterface& ref_buffer, + const I420BufferInterface& test_buffer) { + RTC_DCHECK_GE(ref_buffer.width(), test_buffer.width()); + RTC_DCHECK_GE(ref_buffer.height(), test_buffer.height()); + if ((ref_buffer.width() != test_buffer.width()) || + (ref_buffer.height() != test_buffer.height())) { + rtc::scoped_refptr scaled_ref_buffer = + I420Buffer::Create(test_buffer.width(), test_buffer.height()); + scaled_ref_buffer->ScaleFrom(ref_buffer); + return I420WeightedPSNR(*scaled_ref_buffer, test_buffer); + } + + // Luma. + int width_y = test_buffer.width(); + int height_y = test_buffer.height(); + uint64_t sse_y = libyuv::ComputeSumSquareErrorPlane( + ref_buffer.DataY(), ref_buffer.StrideY(), test_buffer.DataY(), + test_buffer.StrideY(), width_y, height_y); + uint64_t num_samples_y = (uint64_t)width_y * (uint64_t)height_y; + double psnr_y = libyuv::SumSquareErrorToPsnr(sse_y, num_samples_y); + + // Chroma. + int width_uv = (width_y + 1) >> 1; + int height_uv = (height_y + 1) >> 1; + uint64_t sse_u = libyuv::ComputeSumSquareErrorPlane( + ref_buffer.DataU(), ref_buffer.StrideU(), test_buffer.DataU(), + test_buffer.StrideU(), width_uv, height_uv); + uint64_t num_samples_uv = (uint64_t)width_uv * (uint64_t)height_uv; + double psnr_u = libyuv::SumSquareErrorToPsnr(sse_u, num_samples_uv); + uint64_t sse_v = libyuv::ComputeSumSquareErrorPlane( + ref_buffer.DataV(), ref_buffer.StrideV(), test_buffer.DataV(), + test_buffer.StrideV(), width_uv, height_uv); + double psnr_v = libyuv::SumSquareErrorToPsnr(sse_v, num_samples_uv); + + // Weights from Ohm et. al 2012. + double psnr_yuv = (6.0 * psnr_y + psnr_u + psnr_v) / 8.0; + return (psnr_yuv > kPerfectPSNR) ? kPerfectPSNR : psnr_yuv; +} + // Compute SSIM for an I420A frame (all planes). Can upscale test frame. double I420ASSIM(const I420ABufferInterface& ref_buffer, const I420ABufferInterface& test_buffer) { diff --git a/common_video/video_frame_buffer.cc b/common_video/video_frame_buffer.cc index 78a126419a..34bb693897 100644 --- a/common_video/video_frame_buffer.cc +++ b/common_video/video_frame_buffer.cc @@ -124,6 +124,22 @@ rtc::scoped_refptr I444BufferBase::ToI420() { return i420_buffer; } +class I422BufferBase : public I422BufferInterface { + public: + rtc::scoped_refptr ToI420() final; +}; + +rtc::scoped_refptr I422BufferBase::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + libyuv::I422ToI420(DataY(), StrideY(), DataU(), StrideU(), DataV(), StrideV(), + i420_buffer->MutableDataY(), i420_buffer->StrideY(), + i420_buffer->MutableDataU(), i420_buffer->StrideU(), + i420_buffer->MutableDataV(), i420_buffer->StrideV(), + width(), height()); + return i420_buffer; +} + // Template to implement a wrapped buffer for a PlanarYuv16BBuffer. template class WrappedYuv16BBuffer : public Base { @@ -231,6 +247,22 @@ rtc::scoped_refptr WrapI420ABuffer( v_stride, a_plane, a_stride, no_longer_used)); } +rtc::scoped_refptr WrapI422Buffer( + int width, + int height, + const uint8_t* y_plane, + int y_stride, + const uint8_t* u_plane, + int u_stride, + const uint8_t* v_plane, + int v_stride, + std::function no_longer_used) { + return rtc::scoped_refptr( + rtc::make_ref_counted>( + width, height, y_plane, y_stride, u_plane, u_stride, v_plane, + v_stride, no_longer_used)); +} + rtc::scoped_refptr WrapI444Buffer( int width, int height, @@ -262,6 +294,9 @@ rtc::scoped_refptr WrapYuvBuffer( case VideoFrameBuffer::Type::kI420: return WrapI420Buffer(width, height, y_plane, y_stride, u_plane, u_stride, v_plane, v_stride, no_longer_used); + case VideoFrameBuffer::Type::kI422: + return WrapI422Buffer(width, height, y_plane, y_stride, u_plane, u_stride, + v_plane, v_stride, no_longer_used); case VideoFrameBuffer::Type::kI444: return WrapI444Buffer(width, height, y_plane, y_stride, u_plane, u_stride, v_plane, v_stride, no_longer_used); diff --git a/common_video/video_frame_buffer_pool.cc b/common_video/video_frame_buffer_pool.cc index 267cab1a71..c9d6ad23fa 100644 --- a/common_video/video_frame_buffer_pool.cc +++ b/common_video/video_frame_buffer_pool.cc @@ -31,6 +31,10 @@ bool HasOneRef(const rtc::scoped_refptr& buffer) { return static_cast*>(buffer.get()) ->HasOneRef(); } + case VideoFrameBuffer::Type::kI422: { + return static_cast*>(buffer.get()) + ->HasOneRef(); + } case VideoFrameBuffer::Type::kNV12: { return static_cast*>(buffer.get()) ->HasOneRef(); @@ -152,6 +156,37 @@ rtc::scoped_refptr VideoFrameBufferPool::CreateI444Buffer( return buffer; } +rtc::scoped_refptr VideoFrameBufferPool::CreateI422Buffer( + int width, + int height) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); + + rtc::scoped_refptr existing_buffer = + GetExistingBuffer(width, height, VideoFrameBuffer::Type::kI422); + if (existing_buffer) { + // Cast is safe because the only way kI422 buffer is created is + // in the same function below, where |RefCountedObject| + // is created. + rtc::RefCountedObject* raw_buffer = + static_cast*>(existing_buffer.get()); + // Creates a new scoped_refptr, which is also pointing to the same + // RefCountedObject as buffer, increasing ref count. + return rtc::scoped_refptr(raw_buffer); + } + + if (buffers_.size() >= max_number_of_buffers_) + return nullptr; + // Allocate new buffer. + rtc::scoped_refptr buffer = + rtc::make_ref_counted(width, height); + + if (zero_initialize_) + buffer->InitializeData(); + + buffers_.push_back(buffer); + return buffer; +} + rtc::scoped_refptr VideoFrameBufferPool::CreateNV12Buffer( int width, int height) { diff --git a/common_video/video_frame_unittest.cc b/common_video/video_frame_unittest.cc index 9a70f07b34..7c6f2ae4bd 100644 --- a/common_video/video_frame_unittest.cc +++ b/common_video/video_frame_unittest.cc @@ -15,6 +15,8 @@ #include "api/video/i010_buffer.h" #include "api/video/i420_buffer.h" +#include "api/video/i422_buffer.h" +#include "api/video/i444_buffer.h" #include "api/video/nv12_buffer.h" #include "rtc_base/time_utils.h" #include "test/fake_texture_frame.h" @@ -25,114 +27,33 @@ namespace webrtc { namespace { -// Helper class to delegate calls to appropriate container. -class PlanarYuvBufferFactory { - public: - static rtc::scoped_refptr Create(VideoFrameBuffer::Type type, - int width, - int height) { - switch (type) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Create(width, height); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Create(width, height); - default: - RTC_DCHECK_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr Copy(const VideoFrameBuffer& src) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Copy(src); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Copy(*src.GetI010()); - default: - RTC_DCHECK_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr Rotate(const VideoFrameBuffer& src, - VideoRotation rotation) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: - return I420Buffer::Rotate(src, rotation); - case VideoFrameBuffer::Type::kI010: - return I010Buffer::Rotate(*src.GetI010(), rotation); - default: - RTC_DCHECK_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr CropAndScaleFrom( - const VideoFrameBuffer& src, - int offset_x, - int offset_y, - int crop_width, - int crop_height) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: { - rtc::scoped_refptr buffer = - I420Buffer::Create(crop_width, crop_height); - buffer->CropAndScaleFrom(*src.GetI420(), offset_x, offset_y, crop_width, - crop_height); - return buffer; - } - case VideoFrameBuffer::Type::kI010: { - rtc::scoped_refptr buffer = - I010Buffer::Create(crop_width, crop_height); - buffer->CropAndScaleFrom(*src.GetI010(), offset_x, offset_y, crop_width, - crop_height); - return buffer; - } - default: - RTC_DCHECK_NOTREACHED(); - } - return nullptr; - } - - static rtc::scoped_refptr CropAndScaleFrom( - const VideoFrameBuffer& src, - int crop_width, - int crop_height) { - const int out_width = - std::min(src.width(), crop_width * src.height() / crop_height); - const int out_height = - std::min(src.height(), crop_height * src.width() / crop_width); - return CropAndScaleFrom(src, (src.width() - out_width) / 2, - (src.height() - out_height) / 2, out_width, - out_height); - } +struct SubSampling { + int x; + int y; +}; - static rtc::scoped_refptr - ScaleFrom(const VideoFrameBuffer& src, int crop_width, int crop_height) { - switch (src.type()) { - case VideoFrameBuffer::Type::kI420: { - rtc::scoped_refptr buffer = - I420Buffer::Create(crop_width, crop_height); - buffer->ScaleFrom(*src.GetI420()); - return buffer; - } - case VideoFrameBuffer::Type::kI010: { - rtc::scoped_refptr buffer = - I010Buffer::Create(crop_width, crop_height); - buffer->ScaleFrom(*src.GetI010()); - return buffer; - } - default: - RTC_DCHECK_NOTREACHED(); - } - return nullptr; +SubSampling SubSamplingForType(VideoFrameBuffer::Type type) { + switch (type) { + case VideoFrameBuffer::Type::kI420: + return {.x = 2, .y = 2}; + case VideoFrameBuffer::Type::kI420A: + return {.x = 2, .y = 2}; + case VideoFrameBuffer::Type::kI422: + return {.x = 2, .y = 1}; + case VideoFrameBuffer::Type::kI444: + return {.x = 1, .y = 1}; + case VideoFrameBuffer::Type::kI010: + return {.x = 2, .y = 2}; + default: + return {}; } -}; +} -rtc::scoped_refptr CreateGradient(VideoFrameBuffer::Type type, - int width, - int height) { - rtc::scoped_refptr buffer(I420Buffer::Create(width, height)); +// Helper function to create a buffer and fill it with a gradient for +// PlanarYuvBuffer based buffers. +template +rtc::scoped_refptr CreateGradient(int width, int height) { + rtc::scoped_refptr buffer(T::Create(width, height)); // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { @@ -150,13 +71,10 @@ rtc::scoped_refptr CreateGradient(VideoFrameBuffer::Type type, 255 * y / (chroma_height - 1); } } - if (type == VideoFrameBuffer::Type::kI420) - return buffer; - - RTC_DCHECK(type == VideoFrameBuffer::Type::kI010); - return I010Buffer::Copy(*buffer); + return buffer; } +// Helper function to create a buffer and fill it with a gradient. rtc::scoped_refptr CreateNV12Gradient(int width, int height) { rtc::scoped_refptr buffer(NV12Buffer::Create(width, height)); @@ -183,13 +101,17 @@ rtc::scoped_refptr CreateNV12Gradient(int width, // The offsets and sizes describe the rectangle extracted from the // original (gradient) frame, in relative coordinates where the // original frame correspond to the unit square, 0.0 <= x, y < 1.0. -void CheckCrop(const webrtc::I420BufferInterface& frame, +template +void CheckCrop(const T& frame, double offset_x, double offset_y, double rel_width, double rel_height) { int width = frame.width(); int height = frame.height(); + + SubSampling plane_divider = SubSamplingForType(frame.type()); + // Check that pixel values in the corners match the gradient used // for initialization. for (int i = 0; i < 2; i++) { @@ -204,18 +126,23 @@ void CheckCrop(const webrtc::I420BufferInterface& frame, EXPECT_NEAR(frame.DataY()[x + y * frame.StrideY()] / 256.0, (orig_x + orig_y) / 2, 0.02); - EXPECT_NEAR(frame.DataU()[x / 2 + (y / 2) * frame.StrideU()] / 256.0, + EXPECT_NEAR(frame.DataU()[x / plane_divider.x + + (y / plane_divider.y) * frame.StrideU()] / + 256.0, orig_x, 0.02); - EXPECT_NEAR(frame.DataV()[x / 2 + (y / 2) * frame.StrideV()] / 256.0, + EXPECT_NEAR(frame.DataV()[x / plane_divider.x + + (y / plane_divider.y) * frame.StrideV()] / + 256.0, orig_y, 0.02); } } } +template void CheckRotate(int width, int height, webrtc::VideoRotation rotation, - const webrtc::I420BufferInterface& rotated) { + const T& rotated) { int rotated_width = width; int rotated_height = height; @@ -238,15 +165,19 @@ void CheckRotate(int width, } colors[] = {{0, 0, 0}, {127, 255, 0}, {255, 255, 255}, {127, 0, 255}}; int corner_offset = static_cast(rotation) / 90; + SubSampling plane_divider = SubSamplingForType(rotated.type()); + for (int i = 0; i < 4; i++) { int j = (i + corner_offset) % 4; int x = corners[j].x * (rotated_width - 1); int y = corners[j].y * (rotated_height - 1); EXPECT_EQ(colors[i].y, rotated.DataY()[x + y * rotated.StrideY()]); EXPECT_EQ(colors[i].u, - rotated.DataU()[(x / 2) + (y / 2) * rotated.StrideU()]); + rotated.DataU()[(x / plane_divider.x) + + (y / plane_divider.y) * rotated.StrideU()]); EXPECT_EQ(colors[i].v, - rotated.DataV()[(x / 2) + (y / 2) * rotated.StrideV()]); + rotated.DataV()[(x / plane_divider.x) + + (y / plane_divider.y) * rotated.StrideV()]); } } @@ -344,101 +275,147 @@ TEST(TestVideoFrame, TextureInitialValues) { EXPECT_EQ(20, frame.timestamp_us()); } -class TestPlanarYuvBuffer - : public ::testing::TestWithParam {}; +template +class TestPlanarYuvBuffer : public ::testing::Test {}; +TYPED_TEST_SUITE_P(TestPlanarYuvBuffer); -rtc::scoped_refptr CreateAndFillBuffer() { - auto buf = I420Buffer::Create(20, 10); +template +rtc::scoped_refptr CreateAndFillBuffer() { + auto buf = T::Create(20, 10); memset(buf->MutableDataY(), 1, 200); - memset(buf->MutableDataU(), 2, 50); - memset(buf->MutableDataV(), 3, 50); - return buf; -} -TEST_P(TestPlanarYuvBuffer, Copy) { - rtc::scoped_refptr buf1; - switch (GetParam()) { - case VideoFrameBuffer::Type::kI420: { - buf1 = CreateAndFillBuffer(); - break; - } - case VideoFrameBuffer::Type::kI010: { - buf1 = I010Buffer::Copy(*CreateAndFillBuffer()); - break; - } - default: - RTC_DCHECK_NOTREACHED(); + if (buf->type() == VideoFrameBuffer::Type::kI444) { + memset(buf->MutableDataU(), 2, 200); + memset(buf->MutableDataV(), 3, 200); + } else if (buf->type() == VideoFrameBuffer::Type::kI422) { + memset(buf->MutableDataU(), 2, 100); + memset(buf->MutableDataV(), 3, 100); + } else { + memset(buf->MutableDataU(), 2, 50); + memset(buf->MutableDataV(), 3, 50); } - rtc::scoped_refptr buf2 = - PlanarYuvBufferFactory::Copy(*buf1); - EXPECT_TRUE(test::FrameBufsEqual(buf1->ToI420(), buf2->ToI420())); + return buf; } -TEST_P(TestPlanarYuvBuffer, Scale) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); - - // Pure scaling, no cropping. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::ScaleFrom(*buf, 150, 75); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.0, 1.0, 1.0); +TYPED_TEST_P(TestPlanarYuvBuffer, Copy) { + rtc::scoped_refptr buf1 = CreateAndFillBuffer(); + rtc::scoped_refptr buf2 = TypeParam::Copy(*buf1); + EXPECT_TRUE(test::FrameBufsEqual(buf1, buf2)); } -TEST_P(TestPlanarYuvBuffer, CropXCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); +TYPED_TEST_P(TestPlanarYuvBuffer, CropXCenter) { + rtc::scoped_refptr buf = CreateGradient(200, 100); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 50, 0, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.25, 0.0, 0.5, 1.0); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 50, 0, 100, 100); + CheckCrop(*scaled_buffer, 0.25, 0.0, 0.5, 1.0); } -TEST_P(TestPlanarYuvBuffer, CropXNotCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 200, 100); +TYPED_TEST_P(TestPlanarYuvBuffer, CropXNotCenter) { + rtc::scoped_refptr buf = CreateGradient(200, 100); // Non-center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 25, 0, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.125, 0.0, 0.5, 1.0); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 25, 0, 100, 100); + CheckCrop(*scaled_buffer, 0.125, 0.0, 0.5, 1.0); } -TEST_P(TestPlanarYuvBuffer, CropYCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 100, 200); +TYPED_TEST_P(TestPlanarYuvBuffer, CropYCenter) { + rtc::scoped_refptr buf = CreateGradient(100, 200); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 0, 50, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.25, 1.0, 0.5); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 0, 50, 100, 100); + CheckCrop(*scaled_buffer, 0.0, 0.25, 1.0, 0.5); } -TEST_P(TestPlanarYuvBuffer, CropYNotCenter) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 100, 200); +TYPED_TEST_P(TestPlanarYuvBuffer, CropYNotCenter) { + rtc::scoped_refptr buf = CreateGradient(100, 200); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 0, 25, 100, 100); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.125, 1.0, 0.5); + rtc::scoped_refptr scaled_buffer = TypeParam::Create(100, 100); + scaled_buffer->CropAndScaleFrom(*buf, 0, 25, 100, 100); + CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.5); } -TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) { - rtc::scoped_refptr buf = - CreateGradient(GetParam(), 640, 480); +TYPED_TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) { + const int buffer_width = 640; + const int buffer_height = 480; + const int crop_width = 320; + const int crop_height = 180; + rtc::scoped_refptr buf = CreateGradient(640, 480); // Pure center cropping, no scaling. - rtc::scoped_refptr scaled_buffer = - PlanarYuvBufferFactory::CropAndScaleFrom(*buf, 320, 180); - CheckCrop(*scaled_buffer->ToI420(), 0.0, 0.125, 1.0, 0.75); + const int out_width = + std::min(buffer_width, crop_width * buffer_height / crop_height); + const int out_height = + std::min(buffer_height, crop_height * buffer_width / crop_width); + rtc::scoped_refptr scaled_buffer = + TypeParam::Create(out_width, out_height); + scaled_buffer->CropAndScaleFrom(*buf, (buffer_width - out_width) / 2, + (buffer_height - out_height) / 2, out_width, + out_height); + CheckCrop(*scaled_buffer, 0.0, 0.125, 1.0, 0.75); +} + +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBuffer, + Copy, + CropXCenter, + CropXNotCenter, + CropYCenter, + CropYNotCenter, + CropAndScale16x9); + +using TestTypesAll = + ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBuffer, TestTypesAll); + +template +class TestPlanarYuvBufferScale : public ::testing::Test {}; +TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale); + +TYPED_TEST_P(TestPlanarYuvBufferScale, Scale) { + rtc::scoped_refptr buf = CreateGradient(200, 100); + + // Pure scaling, no cropping. + rtc::scoped_refptr scaled_buffer = TypeParam::Create(150, 75); + scaled_buffer->ScaleFrom(*buf); + CheckCrop(*scaled_buffer, 0.0, 0.0, 1.0, 1.0); } -INSTANTIATE_TEST_SUITE_P(All, - TestPlanarYuvBuffer, - ::testing::Values(VideoFrameBuffer::Type::kI420, - VideoFrameBuffer::Type::kI010)); +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale, Scale); + +using TestTypesScale = ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBufferScale, TestTypesScale); + +template +class TestPlanarYuvBufferRotate : public ::testing::Test { + public: + std::vector RotationParams = { + kVideoRotation_0, kVideoRotation_90, kVideoRotation_180, + kVideoRotation_270}; +}; + +TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate); + +TYPED_TEST_P(TestPlanarYuvBufferRotate, Rotates) { + for (const webrtc::VideoRotation& rotation : this->RotationParams) { + rtc::scoped_refptr buffer = CreateGradient(640, 480); + rtc::scoped_refptr rotated_buffer = + TypeParam::Rotate(*buffer, rotation); + CheckRotate(640, 480, rotation, *rotated_buffer); + } +} + +REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate, Rotates); + +using TestTypesRotate = + ::testing::Types; +INSTANTIATE_TYPED_TEST_SUITE_P(Rotate, + TestPlanarYuvBufferRotate, + TestTypesRotate); TEST(TestNV12Buffer, CropAndScale) { const int kSourceWidth = 640; @@ -469,29 +446,6 @@ TEST(TestNV12Buffer, CropAndScale) { kRelativeHeight); } -class TestPlanarYuvBufferRotate - : public ::testing::TestWithParam< - std::tuple> {}; - -TEST_P(TestPlanarYuvBufferRotate, Rotates) { - const webrtc::VideoRotation rotation = std::get<0>(GetParam()); - const VideoFrameBuffer::Type type = std::get<1>(GetParam()); - rtc::scoped_refptr buffer = CreateGradient(type, 640, 480); - rtc::scoped_refptr rotated_buffer = - PlanarYuvBufferFactory::Rotate(*buffer, rotation); - CheckRotate(640, 480, rotation, *rotated_buffer->ToI420()); -} - -INSTANTIATE_TEST_SUITE_P( - Rotate, - TestPlanarYuvBufferRotate, - ::testing::Combine(::testing::Values(kVideoRotation_0, - kVideoRotation_90, - kVideoRotation_180, - kVideoRotation_270), - ::testing::Values(VideoFrameBuffer::Type::kI420, - VideoFrameBuffer::Type::kI010))); - TEST(TestUpdateRect, CanCompare) { VideoFrame::UpdateRect a = {0, 0, 100, 200}; VideoFrame::UpdateRect b = {0, 0, 100, 200}; diff --git a/examples/BUILD.gn b/examples/BUILD.gn index d0571c9bfd..8b84fe480e 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn @@ -15,6 +15,8 @@ if (is_android) { import("//build/config/mac/rules.gni") } else if (is_ios) { import("//build/config/ios/rules.gni") +} else if (is_linux || is_chromeos) { + import("//build/config/linux/pkg_config.gni") } group("examples") { @@ -67,7 +69,12 @@ rtc_library("read_auth_file") { "turnserver/read_auth_file.cc", "turnserver/read_auth_file.h", ] - deps = [ "../rtc_base" ] + deps = [ + "../api:array_view", + "../rtc_base", + "../rtc_base:stringutils", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] } if (rtc_include_tests) { @@ -482,6 +489,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { deps = [ "../api:libjingle_peerconnection_api", "../api:scoped_refptr", + "../api:sequence_checker", "../api/audio_codecs:builtin_audio_decoder_factory", "../api/audio_codecs:builtin_audio_encoder_factory", "../api/rtc_event_log:rtc_event_log_factory", @@ -654,6 +662,18 @@ if (is_ios || (is_mac && target_cpu != "x86")) { } if (is_linux || is_chromeos || is_win) { + if (is_linux || is_chromeos) { + pkg_config("gtk_config") { + packages = [ + # Gtk requires gmodule, but it does not list it as a dependency in some + # misconfigured systems. + "gmodule-2.0", + "gthread-2.0", + "gtk+-3.0", + ] + } + } + rtc_executable("peerconnection_client") { testonly = true sources = [ @@ -680,7 +700,11 @@ if (is_linux || is_chromeos || is_win) { "../p2p:rtc_p2p", "../pc:video_track_source", "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:net_helpers", + "../rtc_base:refcount", + "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base/third_party/sigslot", "../system_wrappers:field_trial", @@ -716,7 +740,7 @@ if (is_linux || is_chromeos || is_win) { "Xext", "Xrender", ] - deps += [ "//build/config/linux/gtk" ] + configs += [ ":gtk_config" ] } deps += [ @@ -733,9 +757,7 @@ if (is_linux || is_chromeos || is_win) { "../modules/audio_processing:api", "../modules/video_capture:video_capture_module", "../pc:libjingle_peerconnection", - "../pc:peerconnection", "../rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_json", "../test:video_test_common", "//third_party/abseil-cpp/absl/flags:flag", @@ -757,7 +779,7 @@ if (is_linux || is_chromeos || is_win) { ] deps = [ "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:stringutils", "../system_wrappers:field_trial", "../test:field_trial", "//third_party/abseil-cpp/absl/flags:flag", @@ -775,7 +797,6 @@ if (is_linux || is_chromeos || is_win) { "../pc:rtc_pc", "../rtc_base", "../rtc_base:ip_address", - "../rtc_base:rtc_base_approved", "../rtc_base:socket_address", "../rtc_base:socket_server", "../rtc_base:threading", @@ -789,7 +810,6 @@ if (is_linux || is_chromeos || is_win) { "../p2p:rtc_p2p", "../pc:rtc_pc", "../rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:socket_address", "../rtc_base:socket_server", "../rtc_base:threading", @@ -838,7 +858,6 @@ if (is_win || is_android) { "../modules/audio_processing:api", "../modules/video_capture:video_capture_module", "../pc:libjingle_peerconnection", - "../pc:peerconnection", "../pc:video_track_source", "../rtc_base", "../test:platform_video_capturer", @@ -915,9 +934,11 @@ if (!build_with_chromium) { "../p2p:rtc_p2p", "../rtc_base", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", "../rtc_base:socket_address", "../rtc_base:threading", + "../rtc_base:timeutils", + "../test:scoped_key_value_config", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] diff --git a/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java b/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java index 5c73b4395c..604e2863d9 100644 --- a/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java +++ b/examples/androidapp/src/org/appspot/apprtc/AppRTCProximitySensor.java @@ -149,16 +149,10 @@ private void logProximitySensorInfo() { info.append(", resolution: ").append(proximitySensor.getResolution()); info.append(", max range: ").append(proximitySensor.getMaximumRange()); info.append(", min delay: ").append(proximitySensor.getMinDelay()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { - // Added in API level 20. - info.append(", type: ").append(proximitySensor.getStringType()); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // Added in API level 21. - info.append(", max delay: ").append(proximitySensor.getMaxDelay()); - info.append(", reporting mode: ").append(proximitySensor.getReportingMode()); - info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); - } + info.append(", type: ").append(proximitySensor.getStringType()); + info.append(", max delay: ").append(proximitySensor.getMaxDelay()); + info.append(", reporting mode: ").append(proximitySensor.getReportingMode()); + info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor()); Log.d(TAG, info.toString()); } } diff --git a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java index 2da2073e2b..eb5ee8289e 100644 --- a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java +++ b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java @@ -384,7 +384,6 @@ public void run() { } } - @TargetApi(17) private DisplayMetrics getDisplayMetrics() { DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager windowManager = @@ -393,16 +392,11 @@ private DisplayMetrics getDisplayMetrics() { return displayMetrics; } - @TargetApi(19) private static int getSystemUiVisibility() { - int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - } - return flags; + return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; } - @TargetApi(21) private void startScreenCapture() { MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getApplication().getSystemService( @@ -460,7 +454,6 @@ private boolean captureToTexture() { return null; } - @TargetApi(21) private @Nullable VideoCapturer createScreenCapturer() { if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) { reportError("User didn't give permission to capture the screen."); diff --git a/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java b/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java index dd51ab2561..1c64621864 100644 --- a/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java +++ b/examples/androidapp/src/org/appspot/apprtc/CpuMonitor.java @@ -73,7 +73,6 @@ * correct value, and then returns to back to correct reading. Both when * jumping up and back down we might create faulty CPU load readings. */ -@TargetApi(Build.VERSION_CODES.KITKAT) class CpuMonitor { private static final String TAG = "CpuMonitor"; private static final int MOVING_AVERAGE_SAMPLES = 5; @@ -159,8 +158,7 @@ public double getAverage() { } public static boolean isSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT - && Build.VERSION.SDK_INT < Build.VERSION_CODES.N; + return Build.VERSION.SDK_INT < Build.VERSION_CODES.N; } public CpuMonitor(Context context) { diff --git a/examples/androidnativeapi/BUILD.gn b/examples/androidnativeapi/BUILD.gn index 1c840f8248..8606f00f32 100644 --- a/examples/androidnativeapi/BUILD.gn +++ b/examples/androidnativeapi/BUILD.gn @@ -48,6 +48,7 @@ if (is_android) { deps = [ ":generated_jni", "../../api:scoped_refptr", + "../../api:sequence_checker", "../../rtc_base/synchronization:mutex", "//api:libjingle_peerconnection_api", "//api/rtc_event_log:rtc_event_log_factory", @@ -58,7 +59,6 @@ if (is_android) { "//modules/utility", "//pc:libjingle_peerconnection", "//rtc_base", - "//rtc_base:rtc_base_approved", "//sdk/android:native_api_base", "//sdk/android:native_api_jni", "//sdk/android:native_api_video", diff --git a/examples/androidnativeapi/jni/android_call_client.cc b/examples/androidnativeapi/jni/android_call_client.cc index 2c5e1af108..7da56e6e60 100644 --- a/examples/androidnativeapi/jni/android_call_client.cc +++ b/examples/androidnativeapi/jni/android_call_client.cc @@ -170,7 +170,7 @@ void AndroidCallClient::CreatePeerConnectionFactory() { RTC_LOG(LS_INFO) << "Media engine created: " << pcf_deps.media_engine.get(); pcf_ = CreateModularPeerConnectionFactory(std::move(pcf_deps)); - RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_; + RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_.get(); } void AndroidCallClient::CreatePeerConnection() { @@ -184,13 +184,13 @@ void AndroidCallClient::CreatePeerConnection() { webrtc::PeerConnectionDependencies deps(pc_observer_.get()); pc_ = pcf_->CreatePeerConnectionOrError(config, std::move(deps)).MoveValue(); - RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_; + RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_.get(); - rtc::scoped_refptr local_video_track = - pcf_->CreateVideoTrack("video", video_source_); + rtc::scoped_refptr local_video_track( + pcf_->CreateVideoTrack("video", video_source_.get())); local_video_track->AddOrUpdateSink(local_sink_.get(), rtc::VideoSinkWants()); pc_->AddTransceiver(local_video_track); - RTC_LOG(LS_INFO) << "Local video sink set up: " << local_video_track; + RTC_LOG(LS_INFO) << "Local video sink set up: " << local_video_track.get(); for (const rtc::scoped_refptr& tranceiver : pc_->GetTransceivers()) { @@ -200,7 +200,7 @@ void AndroidCallClient::CreatePeerConnection() { track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { static_cast(track.get()) ->AddOrUpdateSink(remote_sink_.get(), rtc::VideoSinkWants()); - RTC_LOG(LS_INFO) << "Remote video sink set up: " << track; + RTC_LOG(LS_INFO) << "Remote video sink set up: " << track.get(); break; } } @@ -208,7 +208,7 @@ void AndroidCallClient::CreatePeerConnection() { void AndroidCallClient::Connect() { webrtc::MutexLock lock(&pc_mutex_); - pc_->CreateOffer(rtc::make_ref_counted(pc_), + pc_->CreateOffer(rtc::make_ref_counted(pc_).get(), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -258,7 +258,7 @@ void CreateOfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) { // Ownership of desc was transferred to us, now we transfer it forward. pc_->SetLocalDescription( - rtc::make_ref_counted(), desc); + rtc::make_ref_counted().get(), desc); // Generate a fake answer. std::unique_ptr answer( diff --git a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java index ac3fb23a6e..051d7379bd 100644 --- a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java +++ b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java @@ -453,10 +453,6 @@ public void testLoopbackH264() throws InterruptedException { @Test @SmallTest public void testLoopbackVp8DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP8), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -464,10 +460,6 @@ public void testLoopbackVp8DecodeToTexture() throws InterruptedException { @Test @SmallTest public void testLoopbackVp9DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP9), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -475,10 +467,6 @@ public void testLoopbackVp9DecodeToTexture() throws InterruptedException { @Test @SmallTest public void testLoopbackH264DecodeToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Decode to textures is not supported, requires SDK version 19."); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_H264), createCameraCapturer(false /* captureToTexture */), true /* decodeToTexture */); } @@ -486,10 +474,6 @@ public void testLoopbackH264DecodeToTexture() throws InterruptedException { @Test @SmallTest public void testLoopbackVp8CaptureToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Encode to textures is not supported. Requires SDK version 19"); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_VP8), createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */); } @@ -497,10 +481,6 @@ public void testLoopbackVp8CaptureToTexture() throws InterruptedException { @Test @SmallTest public void testLoopbackH264CaptureToTexture() throws InterruptedException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.i(TAG, "Encode to textures is not supported. Requires KITKAT"); - return; - } doLoopbackTest(createParametersForVideoCall(VIDEO_CODEC_H264), createCameraCapturer(true /* captureToTexture */), true /* decodeToTexture */); } diff --git a/examples/androidvoip/BUILD.gn b/examples/androidvoip/BUILD.gn index 66dde947ac..95b9191a13 100644 --- a/examples/androidvoip/BUILD.gn +++ b/examples/androidvoip/BUILD.gn @@ -56,6 +56,7 @@ if (is_android) { deps = [ ":generated_jni", + "../../rtc_base:logging", "../../rtc_base:socket_address", "../../rtc_base:socket_server", "../../rtc_base:threading", diff --git a/examples/objc/AppRTCMobile/common/ARDUtilities.m b/examples/objc/AppRTCMobile/common/ARDUtilities.m index 119695fccd..e0674f5210 100644 --- a/examples/objc/AppRTCMobile/common/ARDUtilities.m +++ b/examples/objc/AppRTCMobile/common/ARDUtilities.m @@ -94,7 +94,7 @@ + (void)sendAsyncPostToURL:(NSURL *)url @end -NSInteger ARDGetCpuUsagePercentage() { +NSInteger ARDGetCpuUsagePercentage(void) { // Create an array of thread ports for the current task. const task_t task = mach_task_self(); thread_act_array_t thread_array; diff --git a/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm b/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm index 2694e49914..60c8d7ef60 100644 --- a/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm +++ b/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm @@ -161,6 +161,8 @@ - (ARDAppClient *)createAppClientForRoomId:(NSString *)roomId #pragma mark - Cases +// TODO(crbug.com/webrtc/13991): testSession crashes when running on simulators. +#if !TARGET_IPHONE_SIMULATOR // Tests that an ICE connection is established between two ARDAppClient objects // where one is set up as a caller and the other the answerer. Network // components are mocked out and messages are relayed directly from object to @@ -227,6 +229,7 @@ - (void)testSession { } }]; } +#endif // Test to see that we get a local video connection // Note this will currently pass even when no camera is connected as a local diff --git a/examples/objcnativeapi/objc/objc_call_client.mm b/examples/objcnativeapi/objc/objc_call_client.mm index 081b5bc44b..7dd57b499b 100644 --- a/examples/objcnativeapi/objc/objc_call_client.mm +++ b/examples/objcnativeapi/objc/objc_call_client.mm @@ -50,10 +50,9 @@ void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override; }; -class SetLocalSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { +class SetLocalSessionDescriptionObserver : public webrtc::SetLocalDescriptionObserverInterface { public: - void OnSuccess() override; - void OnFailure(webrtc::RTCError error) override; + void OnSetLocalDescriptionComplete(webrtc::RTCError error) override; }; } // namespace @@ -134,7 +133,7 @@ dependencies.event_log_factory = std::make_unique(dependencies.task_queue_factory.get()); pcf_ = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); - RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_; + RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_.get(); } void ObjCCallClient::CreatePeerConnection() { @@ -147,12 +146,12 @@ pcf_->SetOptions(options); webrtc::PeerConnectionDependencies pc_dependencies(pc_observer_.get()); pc_ = pcf_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)).MoveValue(); - RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_; + RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_.get(); rtc::scoped_refptr local_video_track = - pcf_->CreateVideoTrack("video", video_source_); + pcf_->CreateVideoTrack("video", video_source_.get()); pc_->AddTransceiver(local_video_track); - RTC_LOG(LS_INFO) << "Local video sink set up: " << local_video_track; + RTC_LOG(LS_INFO) << "Local video sink set up: " << local_video_track.get(); for (const rtc::scoped_refptr& tranceiver : pc_->GetTransceivers()) { @@ -160,7 +159,7 @@ if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { static_cast(track.get()) ->AddOrUpdateSink(remote_sink_.get(), rtc::VideoSinkWants()); - RTC_LOG(LS_INFO) << "Remote video sink set up: " << track; + RTC_LOG(LS_INFO) << "Remote video sink set up: " << track.get(); break; } } @@ -168,7 +167,7 @@ void ObjCCallClient::Connect() { webrtc::MutexLock lock(&pc_mutex_); - pc_->CreateOffer(rtc::make_ref_counted(pc_), + pc_->CreateOffer(rtc::make_ref_counted(pc_).get(), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -214,7 +213,8 @@ RTC_LOG(LS_INFO) << "Created offer: " << sdp; // Ownership of desc was transferred to us, now we transfer it forward. - pc_->SetLocalDescription(rtc::make_ref_counted(), desc); + pc_->SetLocalDescription(absl::WrapUnique(desc), + rtc::make_ref_counted()); // Generate a fake answer. std::unique_ptr answer( @@ -231,12 +231,8 @@ RTC_LOG(LS_INFO) << "Set remote description: " << error.message(); } -void SetLocalSessionDescriptionObserver::OnSuccess() { - RTC_LOG(LS_INFO) << "Set local description success!"; -} - -void SetLocalSessionDescriptionObserver::OnFailure(webrtc::RTCError error) { - RTC_LOG(LS_INFO) << "Set local description failure: " << error.message(); +void SetLocalSessionDescriptionObserver::OnSetLocalDescriptionComplete(webrtc::RTCError error) { + RTC_LOG(LS_INFO) << "Set local description: " << error.message(); } } // namespace webrtc_examples diff --git a/examples/peerconnection/client/conductor.cc b/examples/peerconnection/client/conductor.cc index 93e95b6583..de0face33a 100644 --- a/examples/peerconnection/client/conductor.cc +++ b/examples/peerconnection/client/conductor.cc @@ -58,7 +58,7 @@ const char kSessionDescriptionSdpName[] = "sdp"; class DummySetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { public: - static DummySetSessionDescriptionObserver* Create() { + static rtc::scoped_refptr Create() { return rtc::make_ref_counted(); } virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; } @@ -190,8 +190,13 @@ bool Conductor::CreatePeerConnection() { server.uri = GetPeerConnectionString(); config.servers.push_back(server); - peer_connection_ = peer_connection_factory_->CreatePeerConnection( - config, nullptr, nullptr, this); + webrtc::PeerConnectionDependencies pc_dependencies(this); + auto error_or_peer_connection = + peer_connection_factory_->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + if (error_or_peer_connection.ok()) { + peer_connection_ = std::move(error_or_peer_connection.value()); + } return peer_connection_ != nullptr; } @@ -241,9 +246,7 @@ void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { return; } - Json::StyledWriter writer; Json::Value jmessage; - jmessage[kCandidateSdpMidName] = candidate->sdp_mid(); jmessage[kCandidateSdpMlineIndexName] = candidate->sdp_mline_index(); std::string sdp; @@ -252,7 +255,9 @@ void Conductor::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { return; } jmessage[kCandidateSdpName] = sdp; - SendMessage(writer.write(jmessage)); + + Json::StreamWriterBuilder factory; + SendMessage(Json::writeString(factory, jmessage)); } // @@ -313,9 +318,12 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { return; } - Json::Reader reader; + Json::CharReaderBuilder factory; + std::unique_ptr reader = + absl::WrapUnique(factory.newCharReader()); Json::Value jmessage; - if (!reader.parse(message, jmessage)) { + if (!reader->parse(message.data(), message.data() + message.length(), + &jmessage, nullptr)) { RTC_LOG(LS_WARNING) << "Received unknown message. " << message; return; } @@ -361,7 +369,7 @@ void Conductor::OnMessageFromPeer(int peer_id, const std::string& message) { } RTC_LOG(LS_INFO) << " Received session description :" << message; peer_connection_->SetRemoteDescription( - DummySetSessionDescriptionObserver::Create(), + DummySetSessionDescriptionObserver::Create().get(), session_description.release()); if (type == webrtc::SdpType::kOffer) { peer_connection_->CreateAnswer( @@ -448,8 +456,9 @@ void Conductor::AddTracks() { rtc::scoped_refptr audio_track( peer_connection_factory_->CreateAudioTrack( - kAudioLabel, peer_connection_factory_->CreateAudioSource( - cricket::AudioOptions()))); + kAudioLabel, + peer_connection_factory_->CreateAudioSource(cricket::AudioOptions()) + .get())); auto result_or_error = peer_connection_->AddTrack(audio_track, {kStreamId}); if (!result_or_error.ok()) { RTC_LOG(LS_ERROR) << "Failed to add audio track to PeerConnection: " @@ -460,8 +469,9 @@ void Conductor::AddTracks() { CapturerTrackSource::Create(); if (video_device) { rtc::scoped_refptr video_track_( - peer_connection_factory_->CreateVideoTrack(kVideoLabel, video_device)); - main_wnd_->StartLocalRenderer(video_track_); + peer_connection_factory_->CreateVideoTrack(kVideoLabel, + video_device.get())); + main_wnd_->StartLocalRenderer(video_track_.get()); result_or_error = peer_connection_->AddTrack(video_track_, {kStreamId}); if (!result_or_error.ok()) { @@ -555,7 +565,7 @@ void Conductor::UIThreadCallback(int msg_id, void* data) { void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) { peer_connection_->SetLocalDescription( - DummySetSessionDescriptionObserver::Create(), desc); + DummySetSessionDescriptionObserver::Create().get(), desc); std::string sdp; desc->ToString(&sdp); @@ -566,17 +576,18 @@ void Conductor::OnSuccess(webrtc::SessionDescriptionInterface* desc) { std::unique_ptr session_description = webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp); peer_connection_->SetRemoteDescription( - DummySetSessionDescriptionObserver::Create(), + DummySetSessionDescriptionObserver::Create().get(), session_description.release()); return; } - Json::StyledWriter writer; Json::Value jmessage; jmessage[kSessionDescriptionTypeName] = webrtc::SdpTypeToString(desc->GetType()); jmessage[kSessionDescriptionSdpName] = sdp; - SendMessage(writer.write(jmessage)); + + Json::StreamWriterBuilder factory; + SendMessage(Json::writeString(factory, jmessage)); } void Conductor::OnFailure(webrtc::RTCError error) { diff --git a/examples/peerconnection/client/linux/main.cc b/examples/peerconnection/client/linux/main.cc index 47f4f3618e..fc76a858ce 100644 --- a/examples/peerconnection/client/linux/main.cc +++ b/examples/peerconnection/client/linux/main.cc @@ -103,7 +103,7 @@ int main(int argc, char* argv[]) { PeerConnectionClient client; auto conductor = rtc::make_ref_counted(&client, &wnd); socket_server.set_client(&client); - socket_server.set_conductor(conductor); + socket_server.set_conductor(conductor.get()); thread.Run(); diff --git a/examples/peerconnection/client/linux/main_wnd.cc b/examples/peerconnection/client/linux/main_wnd.cc index e9b6a514b1..2be75d8f8d 100644 --- a/examples/peerconnection/client/linux/main_wnd.cc +++ b/examples/peerconnection/client/linux/main_wnd.cc @@ -264,20 +264,12 @@ void GtkMainWnd::SwitchToConnectUI() { peer_list_ = NULL; } -#if GTK_MAJOR_VERSION == 2 - vbox_ = gtk_vbox_new(FALSE, 5); -#else vbox_ = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); -#endif GtkWidget* valign = gtk_alignment_new(0, 1, 0, 0); gtk_container_add(GTK_CONTAINER(vbox_), valign); gtk_container_add(GTK_CONTAINER(window_), vbox_); -#if GTK_MAJOR_VERSION == 2 - GtkWidget* hbox = gtk_hbox_new(FALSE, 5); -#else GtkWidget* hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); -#endif GtkWidget* label = gtk_label_new("Server"); gtk_container_add(GTK_CONTAINER(hbox), label); @@ -386,11 +378,7 @@ void GtkMainWnd::OnClicked(GtkWidget* widget) { void GtkMainWnd::OnKeyPress(GtkWidget* widget, GdkEventKey* key) { if (key->type == GDK_KEY_PRESS) { switch (key->keyval) { -#if GTK_MAJOR_VERSION == 2 - case GDK_Escape: -#else case GDK_KEY_Escape: -#endif if (draw_area_) { callback_->DisconnectFromCurrentPeer(); } else if (peer_list_) { @@ -398,13 +386,8 @@ void GtkMainWnd::OnKeyPress(GtkWidget* widget, GdkEventKey* key) { } break; -#if GTK_MAJOR_VERSION == 2 - case GDK_KP_Enter: - case GDK_Return: -#else case GDK_KEY_KP_Enter: case GDK_KEY_Return: -#endif if (vbox_) { OnClicked(NULL); } else if (peer_list_) { @@ -490,21 +473,13 @@ void GtkMainWnd::OnRedraw() { } } -#if GTK_MAJOR_VERSION == 2 - gdk_draw_rgb_32_image(draw_area_->window, - draw_area_->style->fg_gc[GTK_STATE_NORMAL], 0, 0, - width_ * 2, height_ * 2, GDK_RGB_DITHER_MAX, - draw_buffer_.get(), (width_ * 2) * 4); -#else gtk_widget_queue_draw(draw_area_); -#endif } gdk_threads_leave(); } void GtkMainWnd::Draw(GtkWidget* widget, cairo_t* cr) { -#if GTK_MAJOR_VERSION != 2 cairo_format_t format = CAIRO_FORMAT_ARGB32; cairo_surface_t* surface = cairo_image_surface_create_for_data( draw_buffer_.get(), format, width_ * 2, height_ * 2, @@ -513,9 +488,6 @@ void GtkMainWnd::Draw(GtkWidget* widget, cairo_t* cr) { cairo_rectangle(cr, 0, 0, width_ * 2, height_ * 2); cairo_fill(cr); cairo_surface_destroy(surface); -#else - RTC_DCHECK_NOTREACHED(); -#endif } GtkMainWnd::VideoRenderer::VideoRenderer( diff --git a/examples/stunprober/main.cc b/examples/stunprober/main.cc index d0ed92cc34..3b3c06be8f 100644 --- a/examples/stunprober/main.cc +++ b/examples/stunprober/main.cc @@ -26,6 +26,7 @@ #include "rtc_base/ssl_adapter.h" #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" +#include "test/scoped_key_value_config.h" using stunprober::AsyncCallback; using stunprober::StunProber; @@ -123,14 +124,14 @@ int main(int argc, char* argv[]) { rtc::InitializeSSL(); rtc::InitRandom(rtc::Time32()); + webrtc::test::ScopedKeyValueConfig field_trials; rtc::PhysicalSocketServer socket_server; rtc::AutoSocketServerThread thread(&socket_server); auto socket_factory = std::make_unique(&socket_server); std::unique_ptr network_manager( - new rtc::BasicNetworkManager(&socket_server)); - rtc::NetworkManager::NetworkList networks; - network_manager->GetNetworks(&networks); + new rtc::BasicNetworkManager(&socket_server, &field_trials)); + std::vector networks = network_manager->GetNetworks(); auto prober = std::make_unique(socket_factory.get(), rtc::Thread::Current(), networks); auto finish_callback = [&thread](StunProber* prober, int result) { diff --git a/examples/turnserver/read_auth_file.cc b/examples/turnserver/read_auth_file.cc index 3ad5c2bb39..4b0b21b8ae 100644 --- a/examples/turnserver/read_auth_file.cc +++ b/examples/turnserver/read_auth_file.cc @@ -12,6 +12,8 @@ #include +#include "absl/strings/string_view.h" +#include "api/array_view.h" #include "rtc_base/string_encode.h" namespace webrtc_examples { @@ -23,8 +25,8 @@ std::map ReadAuthFile(std::istream* s) { if (sep == std::string::npos) continue; char buf[32]; - size_t len = rtc::hex_decode(buf, sizeof(buf), line.data() + sep + 1, - line.size() - sep - 1); + size_t len = rtc::hex_decode(rtc::ArrayView(buf), + absl::string_view(line).substr(sep + 1)); if (len > 0) { name_to_key.emplace(line.substr(0, sep), std::string(buf, len)); } diff --git a/examples/unityplugin/README b/examples/unityplugin/README index 5f26b89488..da8f07aa11 100644 --- a/examples/unityplugin/README +++ b/examples/unityplugin/README @@ -124,13 +124,13 @@ namespace SimplePeerConnectionM { LocalSdpReadytoSendInternalDelegate callback); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void IceCandiateReadytoSendInternalDelegate( + private delegate void IceCandidateReadytoSendInternalDelegate( string candidate, int sdpMlineIndex, string sdpMid); - public delegate void IceCandiateReadytoSendDelegate( + public delegate void IceCandidateReadytoSendDelegate( int id, string candidate, int sdpMlineIndex, string sdpMid); [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] - private static extern bool RegisterOnIceCandiateReadytoSend( - int peerConnectionId, IceCandiateReadytoSendInternalDelegate callback); + private static extern bool RegisterOnIceCandidateReadytoSend( + int peerConnectionId, IceCandidateReadytoSendInternalDelegate callback); [DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)] private static extern bool SetRemoteDescription(int peerConnectionId, string type, string sdp); @@ -215,10 +215,10 @@ namespace SimplePeerConnectionM { RaiseLocalSdpReadytoSend); RegisterOnLocalSdpReadytoSend(mPeerConnectionId, localSdpReadytoSendDelegate); - iceCandiateReadytoSendDelegate = - new IceCandiateReadytoSendInternalDelegate(RaiseIceCandiateReadytoSend); - RegisterOnIceCandiateReadytoSend( - mPeerConnectionId, iceCandiateReadytoSendDelegate); + iceCandidateReadytoSendDelegate = + new IceCandidateReadytoSendInternalDelegate(RaiseIceCandidateReadytoSend); + RegisterOnIceCandidateReadytoSend( + mPeerConnectionId, iceCandidateReadytoSendDelegate); } private void RaiseLocalDataChannelReady() { @@ -267,9 +267,9 @@ namespace SimplePeerConnectionM { OnLocalSdpReadytoSend(mPeerConnectionId, type, sdp); } - private void RaiseIceCandiateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) { - if (OnIceCandiateReadytoSend != null) - OnIceCandiateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid); + private void RaiseIceCandidateReadytoSend(string candidate, int sdpMlineIndex, string sdpMid) { + if (OnIceCandidateReadytoSend != null) + OnIceCandidateReadytoSend(mPeerConnectionId, candidate, sdpMlineIndex, sdpMid); } public void AddQueuedIceCandidate(List iceCandidateQueue) { @@ -301,8 +301,8 @@ namespace SimplePeerConnectionM { private LocalSdpReadytoSendInternalDelegate localSdpReadytoSendDelegate = null; public event LocalSdpReadytoSendDelegate OnLocalSdpReadytoSend; - private IceCandiateReadytoSendInternalDelegate iceCandiateReadytoSendDelegate = null; - public event IceCandiateReadytoSendDelegate OnIceCandiateReadytoSend; + private IceCandidateReadytoSendInternalDelegate iceCandidateReadytoSendDelegate = null; + public event IceCandidateReadytoSendDelegate OnIceCandidateReadytoSend; private int mPeerConnectionId = -1; } diff --git a/examples/unityplugin/simple_peer_connection.cc b/examples/unityplugin/simple_peer_connection.cc index 16c580e767..861b22f29c 100644 --- a/examples/unityplugin/simple_peer_connection.cc +++ b/examples/unityplugin/simple_peer_connection.cc @@ -98,7 +98,7 @@ std::string GetPeerConnectionString() { class DummySetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver { public: - static DummySetSessionDescriptionObserver* Create() { + static rtc::scoped_refptr Create() { return rtc::make_ref_counted(); } virtual void OnSuccess() { RTC_LOG(LS_INFO) << __FUNCTION__; } @@ -259,7 +259,7 @@ bool SimplePeerConnection::CreateAnswer() { void SimplePeerConnection::OnSuccess( webrtc::SessionDescriptionInterface* desc) { peer_connection_->SetLocalDescription( - DummySetSessionDescriptionObserver::Create(), desc); + DummySetSessionDescriptionObserver::Create().get(), desc); std::string sdp; desc->ToString(&sdp); @@ -286,9 +286,9 @@ void SimplePeerConnection::OnIceCandidate( return; } - if (OnIceCandiateReady) - OnIceCandiateReady(sdp.c_str(), candidate->sdp_mline_index(), - candidate->sdp_mid().c_str()); + if (OnIceCandidateReady) + OnIceCandidateReady(sdp.c_str(), candidate->sdp_mline_index(), + candidate->sdp_mid().c_str()); } void SimplePeerConnection::RegisterOnLocalI420FrameReady( @@ -327,9 +327,9 @@ void SimplePeerConnection::RegisterOnLocalSdpReadytoSend( OnLocalSdpReady = callback; } -void SimplePeerConnection::RegisterOnIceCandiateReadytoSend( +void SimplePeerConnection::RegisterOnIceCandidateReadytoSend( ICECANDIDATEREADYTOSEND_CALLBACK callback) { - OnIceCandiateReady = callback; + OnIceCandidateReady = callback; } bool SimplePeerConnection::SetRemoteDescription(const char* type, @@ -350,7 +350,7 @@ bool SimplePeerConnection::SetRemoteDescription(const char* type, } RTC_LOG(LS_INFO) << " Received session description :" << remote_desc; peer_connection_->SetRemoteDescription( - DummySetSessionDescriptionObserver::Create(), session_description); + DummySetSessionDescriptionObserver::Create().get(), session_description); return true; } @@ -392,8 +392,7 @@ void SimplePeerConnection::SetAudioControl() { if (tracks.empty()) return; - webrtc::AudioTrackInterface* audio_track = tracks[0]; - std::string id = audio_track->id(); + rtc::scoped_refptr& audio_track = tracks[0]; if (is_record_audio_) audio_track->AddSink(this); else @@ -427,9 +426,9 @@ void SimplePeerConnection::AddStreams(bool audio_only) { rtc::scoped_refptr audio_track( g_peer_connection_factory->CreateAudioTrack( - kAudioLabel, g_peer_connection_factory->CreateAudioSource( - cricket::AudioOptions()))); - std::string id = audio_track->id(); + kAudioLabel, + g_peer_connection_factory->CreateAudioSource(cricket::AudioOptions()) + .get())); stream->AddTrack(audio_track); if (!audio_only) { @@ -470,7 +469,7 @@ void SimplePeerConnection::AddStreams(bool audio_only) { if (video_device) { rtc::scoped_refptr video_track( g_peer_connection_factory->CreateVideoTrack(kVideoLabel, - video_device)); + video_device.get())); stream->AddTrack(video_track); } @@ -481,7 +480,7 @@ void SimplePeerConnection::AddStreams(bool audio_only) { } } - if (!peer_connection_->AddStream(stream)) { + if (!peer_connection_->AddStream(stream.get())) { RTC_LOG(LS_ERROR) << "Adding stream to PeerConnection failed"; } diff --git a/examples/unityplugin/simple_peer_connection.h b/examples/unityplugin/simple_peer_connection.h index d5cebc9940..de652ef118 100644 --- a/examples/unityplugin/simple_peer_connection.h +++ b/examples/unityplugin/simple_peer_connection.h @@ -52,7 +52,7 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver, void RegisterOnFailure(FAILURE_CALLBACK callback); void RegisterOnAudioBusReady(AUDIOBUSREADY_CALLBACK callback); void RegisterOnLocalSdpReadytoSend(LOCALSDPREADYTOSEND_CALLBACK callback); - void RegisterOnIceCandiateReadytoSend( + void RegisterOnIceCandidateReadytoSend( ICECANDIDATEREADYTOSEND_CALLBACK callback); bool SetRemoteDescription(const char* type, const char* sdp); bool AddIceCandidate(const char* sdp, @@ -112,7 +112,7 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver, std::unique_ptr local_video_observer_; std::unique_ptr remote_video_observer_; - webrtc::MediaStreamInterface* remote_stream_ = nullptr; + rtc::scoped_refptr remote_stream_ = nullptr; webrtc::PeerConnectionInterface::RTCConfiguration config_; LOCALDATACHANNELREADY_CALLBACK OnLocalDataChannelReady = nullptr; @@ -121,7 +121,7 @@ class SimplePeerConnection : public webrtc::PeerConnectionObserver, AUDIOBUSREADY_CALLBACK OnAudioReady = nullptr; LOCALSDPREADYTOSEND_CALLBACK OnLocalSdpReady = nullptr; - ICECANDIDATEREADYTOSEND_CALLBACK OnIceCandiateReady = nullptr; + ICECANDIDATEREADYTOSEND_CALLBACK OnIceCandidateReady = nullptr; bool is_mute_audio_ = false; bool is_record_audio_ = false; diff --git a/examples/unityplugin/unity_plugin_apis.cc b/examples/unityplugin/unity_plugin_apis.cc index 672330faec..6e34d7e1e0 100644 --- a/examples/unityplugin/unity_plugin_apis.cc +++ b/examples/unityplugin/unity_plugin_apis.cc @@ -184,13 +184,13 @@ bool RegisterOnLocalSdpReadytoSend(int peer_connection_id, return true; } -bool RegisterOnIceCandiateReadytoSend( +bool RegisterOnIceCandidateReadytoSend( int peer_connection_id, ICECANDIDATEREADYTOSEND_CALLBACK callback) { if (!g_peer_connection_map.count(peer_connection_id)) return false; - g_peer_connection_map[peer_connection_id]->RegisterOnIceCandiateReadytoSend( + g_peer_connection_map[peer_connection_id]->RegisterOnIceCandidateReadytoSend( callback); return true; } diff --git a/examples/unityplugin/unity_plugin_apis.h b/examples/unityplugin/unity_plugin_apis.h index 8b8fe0fe80..9790dc57b9 100644 --- a/examples/unityplugin/unity_plugin_apis.h +++ b/examples/unityplugin/unity_plugin_apis.h @@ -100,7 +100,7 @@ WEBRTC_PLUGIN_API bool RegisterOnAudioBusReady(int peer_connection_id, WEBRTC_PLUGIN_API bool RegisterOnLocalSdpReadytoSend( int peer_connection_id, LOCALSDPREADYTOSEND_CALLBACK callback); -WEBRTC_PLUGIN_API bool RegisterOnIceCandiateReadytoSend( +WEBRTC_PLUGIN_API bool RegisterOnIceCandidateReadytoSend( int peer_connection_id, ICECANDIDATEREADYTOSEND_CALLBACK callback); } diff --git a/g3doc/style-guide.md b/g3doc/style-guide.md index 62c99fc73b..421a7d9dc7 100644 --- a/g3doc/style-guide.md +++ b/g3doc/style-guide.md @@ -73,12 +73,12 @@ referencing a WebRTC bug, prefer the url form, e.g. ### Deprecation Annotate the declarations of deprecated functions and classes with the -[`ABSL_DEPRECATED` macro][ABSL_DEPRECATED] to cause an error when they're used +[`[[deprecated]]` attribute][DEPRECATED] to cause an error when they're used inside WebRTC and a compiler warning when they're used by dependant projects. Like so: ```cpp -ABSL_DEPRECATED("bugs.webrtc.org/12345") +[[deprecated("bugs.webrtc.org/12345")]] std::pony PonyPlz(const std::pony_spec& ps); ``` @@ -90,7 +90,7 @@ getting errors, do something like this: ```cpp std::pony DEPRECATED_PonyPlz(const std::pony_spec& ps); -ABSL_DEPRECATED("bugs.webrtc.org/12345") +[[deprecated("bugs.webrtc.org/12345")]] inline std::pony PonyPlz(const std::pony_spec& ps) { return DEPRECATED_PonyPlz(ps); } @@ -100,6 +100,12 @@ 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. +NOTE 3: Occasionally, with long descriptions, `git cl format` will do the wrong +thing with the attribute. In that case, you can use the +[`ABSL_DEPRECATED` macro][ABSL_DEPRECATED], which is formatted in a more +readable way. + +[DEPRECATED]: https://en.cppreference.com/w/cpp/language/attributes/deprecated [ABSL_DEPRECATED]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/abseil-cpp/absl/base/attributes.h?q=ABSL_DEPRECATED ### ArrayView diff --git a/g3doc/supported-platforms-and-compilers.md b/g3doc/supported-platforms-and-compilers.md index 9e51a29ab7..aa393bbda4 100644 --- a/g3doc/supported-platforms-and-compilers.md +++ b/g3doc/supported-platforms-and-compilers.md @@ -29,8 +29,6 @@ See also [here](https://source.chromium.org/chromium/chromium/src/+/main:docs/clang.md) for some clang related documentation from Chromium. -MSVC is also supported at version VS 2019 16.61. - Other compilers are not officially supported (which means there is no CI coverage for them) but patches to keep WebRTC working with them are welcomed by the WebRTC Team. diff --git a/infra/OWNERS b/infra/OWNERS new file mode 100644 index 0000000000..eae8171db5 --- /dev/null +++ b/infra/OWNERS @@ -0,0 +1,6 @@ +mbonadei@webrtc.org +jleconte@webrtc.org +titovartem@webrtc.org +jansson@webrtc.org +terelius@webrtc.org +landrey@webrtc.org diff --git a/infra/config/PRESUBMIT.py b/infra/config/PRESUBMIT.py new file mode 100644 index 0000000000..6aa75c7df5 --- /dev/null +++ b/infra/config/PRESUBMIT.py @@ -0,0 +1,20 @@ +# 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. + + +def CheckChangeOnUpload(input_api, output_api): + return input_api.RunTests( + input_api.canned_checks.CheckLucicfgGenOutput( + input_api, output_api, + 'config.star')) + input_api.canned_checks.CheckChangedLUCIConfigs( + input_api, output_api) + return res + + +def CheckChangeOnCommit(input_api, output_api): + return CheckChangeOnUpload(input_api, output_api) diff --git a/infra/config/README.md b/infra/config/README.md new file mode 100644 index 0000000000..ef60c763db --- /dev/null +++ b/infra/config/README.md @@ -0,0 +1,52 @@ +# infra/config folder + +This folder contains WebRTC project-wide configurations for Chrome infra +services, mainly the CI system ([console][]). + +`*.cfg` files are the actual configuration that [LUCI][luci-config] looks at. +They are in *protocol buffer text format*. For example, +[cr-buildbucket.cfg](cr-buildbucket.cfg) defines builders. + +However, they are all automatically generated from the [Starlark][] script +[config.star](config.star) that defines a unified config using **[lucicfg][]**. +The main body of the config is at the bottom of the file, following all the +helper definitions. + +`lucicfg` should be available as part of depot_tools. After editing +[config.star](config.star) you should run `lucicfg generate config.star` to +re-generate `*.cfg` files. Check the diffs in generated files to confirm that +your change worked as expected. Both the code change and the generated changes +need to be committed together. + +## Uploading changes + +It is recommended to have a separate checkout for this branch, so switching +to/from it does not populate/delete all files in the master branch. + +Initial setup: + +```bash +git clone https://webrtc.googlesource.com/src/ +``` + +Now you can create a new branch to make changes: + +```bash +git new-branch add-new-builder +# edit/generate files +git commit -a +git cl upload +``` + +Changes can be reviewed on Gerrit and submitted with commit queue as usual. + +### Activating the changes + +Any changes to this directory go live soon after landing, without any additional +steps. You can see the status or force a refresh of the config at +[luci-config][]. + +[console]: https://ci.chromium.org/p/webrtc/g/ci/console +[luci-config]: https://luci-config.appspot.com/#/projects/webrtc +[starlark]: https://github.com/google/starlark-go +[lucicfg]: https://chromium.googlesource.com/infra/luci/luci-go/+/master/lucicfg/doc/ diff --git a/infra/config/chops-weetbix-dev.cfg b/infra/config/chops-weetbix-dev.cfg new file mode 100644 index 0000000000..eaef9eae73 --- /dev/null +++ b/infra/config/chops-weetbix-dev.cfg @@ -0,0 +1,70 @@ +# Schema for this config file: ProjectConfig in: +# https://luci-config.appspot.com/schemas/projects:chops-weetbix.cfg +bug_filing_threshold { + presubmit_runs_failed { + # clusters blocking developers should have bugs filed. + one_day: 3 + } + test_runs_failed { + # clusters that aren't blocking developers but are failing a significant + # amount of tasks should have bugs filed to look into optimizing machine + # resource usage. + one_day: 500 + } +} +clustering { + test_name_rules { + name: "Tast Tests" + pattern: "^tast\\.(?P([^.]+))\\.(?P([^.]+))\\..*$" + like_template: "tast.${suite}.${testname}.%" + } +} +monorail { + project: "chromium" + default_field_values { + # Type field. + field_id: 10 + value: "Bug" + } + priority_field_id: 11 + priorities { + priority: "0" + threshold { + presubmit_runs_failed { + one_day: 20 + } + } + } + priorities { + priority: "1" + threshold { + presubmit_runs_failed { + one_day: 10 + } + } + } + priorities { + priority: "2" + threshold { + presubmit_runs_failed { + one_day: 2 + } + } + } + priorities { + priority: "3" + threshold { + # Clusters which fail to meet this threshold will be closed. + test_runs_failed { + one_day: 2 + } + presubmit_runs_failed { + one_day: 1 + seven_day: 1 + } + } + } + priority_hysteresis_percent: 50 + monorail_hostname: "monorail-staging.appspot.com" + display_prefix: "crbug.com" +} diff --git a/infra/config/chops-weetbix.cfg b/infra/config/chops-weetbix.cfg new file mode 100644 index 0000000000..9c9f153e00 --- /dev/null +++ b/infra/config/chops-weetbix.cfg @@ -0,0 +1,71 @@ +# Schema for this config file: ProjectConfig in: +# https://luci-config.appspot.com/schemas/projects:chops-weetbix.cfg +bug_filing_threshold { + presubmit_runs_failed { + # clusters blocking developers should have bugs filed. + one_day: 3 + } + test_runs_failed { + # clusters that aren't blocking developers but are failing a significant + # amount of tasks should have bugs filed to look into optimizing machine + # resource usage. + one_day: 500 + } +} +clustering { + test_name_rules { + name: "Tast Tests" + pattern: "^tast\\.(?P([^.]+))\\.(?P([^.]+))\\..*$" + like_template: "tast.${suite}.${testname}.%" + } +} +monorail { + project: "chromium" + default_field_values { + # Type field. + field_id: 10 + value: "Bug" + } + priority_field_id: 11 + priorities { + priority: "0" + threshold { + presubmit_runs_failed { + one_day: 20 + } + } + } + priorities { + priority: "1" + threshold { + presubmit_runs_failed { + one_day: 10 + } + } + } + priorities { + priority: "2" + threshold { + presubmit_runs_failed { + one_day: 2 + } + } + } + priorities { + priority: "3" + threshold { + # Clusters which fail to meet this threshold will be closed. + test_runs_failed { + one_day: 2 + } + presubmit_runs_failed { + one_day: 1 + seven_day: 1 + } + } + } + priority_hysteresis_percent: 50 + monorail_hostname: "bugs.chromium.org" + display_prefix: "crbug.com" +} + diff --git a/infra/config/codereview.settings b/infra/config/codereview.settings new file mode 100644 index 0000000000..df15400f01 --- /dev/null +++ b/infra/config/codereview.settings @@ -0,0 +1,6 @@ +# This file is used by git-cl to get repository specific information. +CODE_REVIEW_SERVER: codereview.webrtc.org +CC_LIST: webrtc-reviews@webrtc.org +GERRIT_HOST: True +PROJECT: webrtc +VIEW_VC: https://webrtc.googlesource.com/src/+/ diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg new file mode 100644 index 0000000000..ac8c474e27 --- /dev/null +++ b/infra/config/commit-queue.cfg @@ -0,0 +1,365 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Config message: +# https://luci-config.appspot.com/schemas/projects:commit-queue.cfg + +cq_status_host: "chromium-cq-status.appspot.com" +submit_options { + max_burst: 1 + burst_delay { + seconds: 60 + } +} +config_groups { + name: "cq" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/heads/master" + ref_regexp: "refs/heads/main" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-committers" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tree_status { + url: "https://webrtc-status.appspot.com" + } + tryjob { + builders { + name: "webrtc-internal/g3.webrtc-internal.try/internal_compile_lite" + owner_whitelist_group: "project-webrtc-internal-tryjob-access" + } + builders { + name: "webrtc/try/android_arm64_rel" + } + builders { + name: "webrtc/try/android_arm_dbg" + } + builders { + name: "webrtc/try/android_arm_more_configs" + } + builders { + name: "webrtc/try/android_arm_rel" + } + builders { + name: "webrtc/try/android_chromium_compile" + } + builders { + name: "webrtc/try/android_compile_arm64_rel" + } + builders { + name: "webrtc/try/android_compile_arm_rel" + } + builders { + name: "webrtc/try/android_compile_x64_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_rel" + } + builders { + name: "webrtc/try/ios_api_framework" + } + builders { + name: "webrtc/try/ios_compile_arm64_dbg" + } + builders { + name: "webrtc/try/ios_compile_arm64_rel" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios12" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios13" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios14" + } + builders { + name: "webrtc/try/linux_asan" + } + builders { + name: "webrtc/try/linux_chromium_compile" + } + builders { + name: "webrtc/try/linux_chromium_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_rel" + } + builders { + name: "webrtc/try/linux_compile_arm_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm_rel" + } + builders { + name: "webrtc/try/linux_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_rel" + } + builders { + name: "webrtc/try/linux_libfuzzer_rel" + } + builders { + name: "webrtc/try/linux_more_configs" + } + builders { + name: "webrtc/try/linux_msan" + } + builders { + name: "webrtc/try/linux_rel" + } + builders { + name: "webrtc/try/linux_tsan2" + } + builders { + name: "webrtc/try/linux_ubsan" + } + builders { + name: "webrtc/try/linux_ubsan_vptr" + } + builders { + name: "webrtc/try/linux_x86_dbg" + } + builders { + name: "webrtc/try/linux_x86_rel" + } + builders { + name: "webrtc/try/mac_asan" + } + builders { + name: "webrtc/try/mac_chromium_compile" + } + builders { + name: "webrtc/try/mac_compile_dbg" + } + builders { + name: "webrtc/try/mac_rel" + } + builders { + name: "webrtc/try/presubmit" + disable_reuse: true + } + builders { + name: "webrtc/try/win_asan" + } + builders { + name: "webrtc/try/win_chromium_compile" + } + builders { + name: "webrtc/try/win_chromium_compile_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_rel" + } + builders { + name: "webrtc/try/win_compile_x86_clang_dbg" + } + builders { + name: "webrtc/try/win_x86_clang_rel" + } + builders { + name: "webrtc/try/win_x86_more_configs" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} +config_groups { + name: "cq_branch" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/branch-heads/.+" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-committers" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tryjob { + builders { + name: "webrtc/try/android_arm64_rel" + } + builders { + name: "webrtc/try/android_arm_dbg" + } + builders { + name: "webrtc/try/android_arm_more_configs" + } + builders { + name: "webrtc/try/android_arm_rel" + } + builders { + name: "webrtc/try/android_compile_arm64_rel" + } + builders { + name: "webrtc/try/android_compile_arm_rel" + } + builders { + name: "webrtc/try/android_compile_x64_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_dbg" + } + builders { + name: "webrtc/try/android_compile_x86_rel" + } + builders { + name: "webrtc/try/ios_api_framework" + } + builders { + name: "webrtc/try/ios_compile_arm64_dbg" + } + builders { + name: "webrtc/try/ios_compile_arm64_rel" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios12" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios13" + } + builders { + name: "webrtc/try/ios_sim_x64_dbg_ios14" + } + builders { + name: "webrtc/try/linux_asan" + } + builders { + name: "webrtc/try/linux_compile_arm64_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm64_rel" + } + builders { + name: "webrtc/try/linux_compile_arm_dbg" + } + builders { + name: "webrtc/try/linux_compile_arm_rel" + } + builders { + name: "webrtc/try/linux_compile_dbg" + } + builders { + name: "webrtc/try/linux_compile_rel" + } + builders { + name: "webrtc/try/linux_libfuzzer_rel" + } + builders { + name: "webrtc/try/linux_more_configs" + } + builders { + name: "webrtc/try/linux_msan" + } + builders { + name: "webrtc/try/linux_rel" + } + builders { + name: "webrtc/try/linux_tsan2" + } + builders { + name: "webrtc/try/linux_ubsan" + } + builders { + name: "webrtc/try/linux_ubsan_vptr" + } + builders { + name: "webrtc/try/linux_x86_dbg" + } + builders { + name: "webrtc/try/linux_x86_rel" + } + builders { + name: "webrtc/try/mac_asan" + } + builders { + name: "webrtc/try/mac_compile_dbg" + } + builders { + name: "webrtc/try/mac_rel" + } + builders { + name: "webrtc/try/presubmit" + disable_reuse: true + } + builders { + name: "webrtc/try/win_asan" + } + builders { + name: "webrtc/try/win_compile_x64_clang_dbg" + } + builders { + name: "webrtc/try/win_compile_x64_clang_rel" + } + builders { + name: "webrtc/try/win_compile_x86_clang_dbg" + } + builders { + name: "webrtc/try/win_x86_clang_rel" + } + builders { + name: "webrtc/try/win_x86_more_configs" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} +config_groups { + name: "cq_infra" + gerrit { + url: "https://webrtc-review.googlesource.com" + projects { + name: "src" + ref_regexp: "refs/heads/infra/config" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-webrtc-admins" + dry_run_access_list: "project-webrtc-tryjob-access" + } + tryjob { + builders { + name: "webrtc/try/presubmit" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} diff --git a/infra/config/config.star b/infra/config/config.star new file mode 100755 index 0000000000..e8e753ff68 --- /dev/null +++ b/infra/config/config.star @@ -0,0 +1,863 @@ +#!/usr/bin/env lucicfg + +# 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. + +# https://chromium.googlesource.com/infra/luci/luci-go/+/main/lucicfg/doc/ + +"""LUCI project configuration for WebRTC CQ and CI.""" + +lucicfg.check_version("1.30.9") + +WEBRTC_GIT = "https://webrtc.googlesource.com/src" +WEBRTC_GERRIT = "https://webrtc-review.googlesource.com/src" +WEBRTC_TROOPER_EMAIL = "webrtc-troopers-robots@google.com" +WEBRTC_XCODE13 = "13c100" +DEFAULT_CPU = "x86-64" + +# Helpers: + +def make_goma_properties(enable_ats = True, jobs = None): + """Makes a default goma property with the specified argument. + + Args: + enable_ats: True if the ATS should be enabled. + jobs: Number of jobs to be used by the builder. + Returns: + A dictonary with the goma properties. + """ + goma_properties = { + "server_host": "goma.chromium.org", + "use_luci_auth": True, + } + if not enable_ats: + goma_properties["enable_ats"] = enable_ats + if jobs: + goma_properties["jobs"] = jobs + return {"$build/goma": goma_properties} + +# Add names of builders to remove from LKGR finder to this list. This is +# useful when a failure can be safely ignored while fixing it without +# blocking the LKGR finder on it. +skipped_lkgr_bots = [ +] + +# Use LUCI Scheduler BBv2 names and add Scheduler realms configs. +lucicfg.enable_experiment("crbug.com/1182002") + +luci.builder.defaults.experiments.set( + { + "luci.recipes.use_python3": 100, + }, +) +luci.builder.defaults.test_presentation.set( + resultdb.test_presentation(grouping_keys = ["status", "v.test_suite"]), +) + +lucicfg.config( + config_dir = ".", + tracked_files = [ + "chops-weetbix-dev.cfg", + "chops-weetbix.cfg", + "commit-queue.cfg", + "cr-buildbucket.cfg", + "luci-logdog.cfg", + "luci-milo.cfg", + "luci-notify.cfg", + "luci-notify/**/*", + "luci-scheduler.cfg", + "project.cfg", + "realms.cfg", + ], + lint_checks = ["default"], +) + +luci.project( + name = "webrtc", + buildbucket = "cr-buildbucket.appspot.com", + logdog = "luci-logdog.appspot.com", + milo = "luci-milo.appspot.com", + notify = "luci-notify.appspot.com", + scheduler = "luci-scheduler.appspot.com", + swarming = "chromium-swarm.appspot.com", + acls = [ + acl.entry( + [acl.BUILDBUCKET_READER, acl.LOGDOG_READER, acl.PROJECT_CONFIGS_READER, acl.SCHEDULER_READER], + groups = ["all"], + ), + acl.entry(acl.LOGDOG_WRITER, groups = ["luci-logdog-chromium-writers"]), + acl.entry(acl.SCHEDULER_OWNER, groups = ["project-webrtc-admins"]), + ], + bindings = [ + luci.binding( + roles = "role/configs.validator", + users = [ + "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com", + ], + ), + luci.binding( + roles = "role/swarming.poolOwner", + groups = "project-webrtc-admins", + ), + luci.binding( + roles = "role/swarming.poolViewer", + groups = "all", + ), + # Allow any WebRTC build to trigger a test ran under chromium-tester@ + # task service account. + luci.binding( + roles = "role/swarming.taskServiceAccount", + users = [ + "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + ], + ), + ], +) + +luci.logdog( + gs_bucket = "chromium-luci-logdog", +) + +luci.milo( + logo = "https://storage.googleapis.com/chrome-infra/webrtc-logo-vert-retro-255x305.png", +) + +# Configure Weetbix (config is copied verbatim) +################################################################################ + +lucicfg.emit( + dest = "chops-weetbix-dev.cfg", + data = io.read_file("chops-weetbix-dev.cfg"), +) + +lucicfg.emit( + dest = "chops-weetbix.cfg", + data = io.read_file("chops-weetbix.cfg"), +) + +################################################################################ + +luci.notify(tree_closing_enabled = True) + +luci.cq( + status_host = "chromium-cq-status.appspot.com", + submit_max_burst = 1, + submit_burst_delay = 1 * time.minute, +) + +luci.gitiles_poller( + name = "webrtc-gitiles-trigger-main", + bucket = "ci", + repo = WEBRTC_GIT, + refs = ["refs/heads/main"], +) + +# Swarming permissions: + +luci.realm(name = "pools/cron", bindings = [ + # Unlike WebRTC's own builders, other projects need an explicit grant to use this pool. + luci.binding( + roles = "role/swarming.poolUser", + projects = "libyuv", + ), +]) + +luci.realm(name = "pools/ci") +luci.realm(name = "pools/ci-tests", bindings = [ + # Allow task service accounts of .ci pool/bucket to trigger tasks here. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-ci-task-accounts", + ), + # Allow tasks here to use .ci task service accounts. + luci.binding( + roles = "role/swarming.taskServiceAccount", + groups = "project-webrtc-ci-task-accounts", + ), +]) +luci.realm(name = "ci", bindings = [ + # Allow CI builders to create invocations in their own builds. + luci.binding( + roles = "role/resultdb.invocationCreator", + groups = "project-webrtc-ci-task-accounts", + ), +]) + +luci.realm(name = "pools/try", bindings = [ + # Allow to use LED & Swarming "Debug" feature to a larger group but only on try bots / builders. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-led-users", + ), +]) +luci.realm(name = "pools/try-tests", bindings = [ + # Allow task service accounts of .try pool/bucket to trigger tasks here. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-try-task-accounts", + ), + # Allow tasks here to use .try task service accounts. + luci.binding( + roles = "role/swarming.taskServiceAccount", + groups = "project-webrtc-try-task-accounts", + ), +]) +luci.realm(name = "try", bindings = [ + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-led-users", + ), + # Allow try builders to create invocations in their own builds. + luci.binding( + roles = "role/resultdb.invocationCreator", + groups = "project-webrtc-try-task-accounts", + ), +]) + +luci.realm(name = "pools/perf", bindings = [ + # Allow to use LED & Swarming "Debug" feature to a larger group but only on perf bots / builders. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-led-users", + ), +]) +luci.realm(name = "perf", bindings = [ + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-led-users", + ), +]) + +luci.realm(name = "@root", bindings = [ + # Allow admins to use LED & Swarming "Debug" feature on all WebRTC bots. + luci.binding( + roles = "role/swarming.poolUser", + groups = "project-webrtc-admins", + ), + luci.binding( + roles = "role/swarming.taskTriggerer", + groups = "project-webrtc-admins", + ), +]) + +# Bucket definitions: + +luci.bucket( + name = "try", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "service-account-cq", + "project-webrtc-tryjob-access", + ]), + ], +) + +luci.bucket( + name = "ci", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + "project-webrtc-ci-schedulers", + ]), + acl.entry(acl.BUILDBUCKET_TRIGGERER, groups = [ + # Allow Pinpoint to trigger builds for bisection + "service-account-chromeperf", + ]), + ], +) + +luci.bucket( + name = "perf", + acls = [ + acl.entry(acl.BUILDBUCKET_TRIGGERER, users = [ + "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + ]), + ], +) + +luci.bucket( + name = "cron", +) + +# Commit queue definitions: + +luci.cq_group( + name = "cq", + tree_status_host = "webrtc-status.appspot.com", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/heads/master", "refs/heads/main"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-committers"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_group( + name = "cq_branch", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/branch-heads/.+"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-committers"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_group( + name = "cq_infra", + watch = [cq.refset(repo = WEBRTC_GERRIT, refs = ["refs/heads/infra/config"])], + acls = [ + acl.entry(acl.CQ_COMMITTER, groups = ["project-webrtc-admins"]), + acl.entry(acl.CQ_DRY_RUNNER, groups = ["project-webrtc-tryjob-access"]), + ], + retry_config = cq.RETRY_ALL_FAILURES, + cancel_stale_tryjobs = True, +) + +luci.cq_tryjob_verifier( + builder = "presubmit", + cq_group = "cq_infra", +) + +luci.cq_tryjob_verifier( + builder = "webrtc-internal:g3.webrtc-internal.try/internal_compile_lite", + owner_whitelist = ["project-webrtc-internal-tryjob-access"], + cq_group = "cq", +) + +# Notifier definitions: + +luci.notifier( + name = "post_submit_failure_notifier", + on_new_status = ["FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + notify_blamelist = True, + template = luci.notifier_template( + name = "build_failure", + body = io.read_file("luci-notify/email-templates/build_failure.template"), + ), +) + +luci.notifier( + name = "cron_notifier", + on_new_status = ["FAILURE", "INFRA_FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + template = luci.notifier_template( + name = "cron", + body = io.read_file("luci-notify/email-templates/cron.template"), + ), +) + +luci.notifier( + name = "infra_failure_notifier", + on_new_status = ["INFRA_FAILURE"], + notify_emails = [WEBRTC_TROOPER_EMAIL], + template = luci.notifier_template( + name = "infra_failure", + body = io.read_file("luci-notify/email-templates/infra_failure.template"), + ), +) + +# Tree closer definitions: + +luci.tree_closer( + name = "webrtc_tree_closer", + tree_status_host = "webrtc-status.appspot.com", + # TODO: These step filters are copied verbatim from Gatekeeper, for testing + # that LUCI-Notify would take the exact same actions. Once we've switched + # over, this should be updated - several of these steps don't exist in + # WebRTC recipes. + failed_step_regexp = [ + "bot_update", + "compile", + "gclient runhooks", + "runhooks", + "update", + "extract build", + "cleanup_temp", + "taskkill", + "compile", + "gn", + ], + failed_step_regexp_exclude = ".*\\(experimental\\).*", +) + +# Recipe definitions: + +def recipe(recipe, pkg = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"): + return luci.recipe( + name = recipe.split("/")[-1], + cipd_package = pkg, + cipd_version = "refs/heads/main", + recipe = recipe, + use_python3 = True, + ) + +recipe("chromium_trybot") +recipe("run_presubmit") +recipe("webrtc/auto_roll_webrtc_deps") +recipe("webrtc/ios_api_framework") +recipe("webrtc/libfuzzer") +recipe("webrtc/standalone") +recipe("webrtc/update_webrtc_binary_version") +recipe("lkgr_finder", pkg = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build") + +# Console definitions: + +luci.console_view(name = "ci", title = "Main", repo = WEBRTC_GIT, header = "console-header.textpb", refs = ["refs/heads/master", "refs/heads/main"]) +luci.console_view(name = "perf", title = "Perf", repo = WEBRTC_GIT, header = "console-header.textpb", refs = ["refs/heads/master", "refs/heads/main"]) +luci.list_view(name = "cron", title = "Cron") +luci.list_view(name = "try", title = "Tryserver") + +def add_milo(builder, views): + """Add Milo console entries for the builder. + + Args: + builder: builder name (str). + views: dict where keys are names of consoles and values are either a + category for the console (str, pipe-separated) or True, which means + adding to a list view rather than a console. + """ + for view_name, category in views.items(): + if category == None: + continue + elif type(category) == "string": + category, _, short_name = category.rpartition("|") + luci.console_view_entry( + console_view = view_name, + builder = builder, + category = category or None, + short_name = short_name or None, + ) + elif category == True: + luci.list_view_entry( + list_view = view_name, + builder = builder, + ) + else: + fail("Unexpected value for category: %r" % category) + +lkgr_builders = [] + +# Builder-defining functions: + +def webrtc_builder( + name, + bucket, + dimensions, + properties = None, + recipe = "standalone", + priority = 30, + execution_timeout = 2 * time.hour, + **kwargs): + """WebRTC specific wrapper around luci.builder. + + Args: + name: builder name (str). + bucket: The name of the bucket the builder belongs to. + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + recipe: string with the name of the recipe to run. + priority: int [1-255] or None, indicating swarming task priority, lower is + more important. If None, defer the decision to Buildbucket service. + execution_timeout: int or None, how long to wait for a running build to finish before + forcefully aborting it and marking the build as timed out. If None, + defer the decision to Buildbucket service. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + """ + properties = properties or {} + properties["$recipe_engine/isolated"] = { + "server": "https://isolateserver.appspot.com", + } + resultdb_bq_table = "webrtc-ci.resultdb." + bucket + "_test_results" + return luci.builder( + name = name, + bucket = bucket, + executable = recipe, + dimensions = dimensions, + properties = properties, + execution_timeout = execution_timeout, + priority = priority, + build_numbers = True, + swarming_tags = ["vpython:native-python-wrapper"], + resultdb_settings = resultdb.settings( + enable = True, + bq_exports = [ + resultdb.export_test_results(bq_table = resultdb_bq_table), + ], + ), + **kwargs + ) + +def ci_builder( + name, + ci_cat, + dimensions, + properties = None, + perf_cat = None, + prioritized = False, + enabled = True, + **kwargs): + """Add a post-submit builder. + + Args: + name: builder name (str). + ci_cat: the category + name for the /ci/ console, or None to omit from the console. + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + perf_cat: the category + name for the /perf/ console, or None to omit from the console. + prioritized: True to make this builder have a higher priority and never batch builds. + enabled: False to exclude this builder from consoles and failure notifications. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + + Notifications are also disabled if a builder is not on either of /ci/ or /perf/ consoles. + """ + if prioritized: + kwargs["triggering_policy"] = scheduler.greedy_batching( + max_batch_size = 1, + max_concurrent_invocations = 3, + ) + kwargs["priority"] = 29 + + if enabled: + add_milo(name, {"ci": ci_cat, "perf": perf_cat}) + if ci_cat and not perf_cat: + lkgr_builders.append(name) + dimensions.update({"pool": "luci.webrtc.ci", "cpu": kwargs.pop("cpu", DEFAULT_CPU)}) + properties = properties or {} + properties["builder_group"] = "client.webrtc" + properties.update(make_goma_properties()) + notifies = ["post_submit_failure_notifier", "infra_failure_notifier"] + notifies += ["webrtc_tree_closer"] if name not in skipped_lkgr_bots else [] + return webrtc_builder( + name = name, + dimensions = dimensions, + properties = properties, + bucket = "perf" if perf_cat else "ci", + service_account = "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + triggered_by = ["webrtc-gitiles-trigger-main"] if enabled else None, + repo = WEBRTC_GIT, + notifies = notifies if enabled else None, + **kwargs + ) + +def try_builder( + name, + dimensions, + properties = None, + try_cat = True, + cq = {}, + branch_cq = True, + goma_enable_ats = True, + goma_jobs = None, + **kwargs): + """Add a pre-submit builder. + + Args: + name: builder name (str). + dimensions: dict of Swarming dimensions (strings) to search machines by. + properties: dict of properties to pass to the recipe (on top of the default ones). + try_cat: boolean, whether to include this builder in the /try/ console. See also: `add_milo`. + cq: None to exclude this from all commit queues, or a dict of kwargs for cq_tryjob_verifier. + branch_cq: False to exclude this builder just from the release-branch CQ. + goma_enable_ats: True if the ATS should be enabled by the builder. + goma_jobs: Number of jobs to be used by the builder. + **kwargs: Pass on to webrtc_builder / luci.builder. + Returns: + A luci.builder. + """ + add_milo(name, {"try": try_cat}) + dimensions.update({"pool": "luci.webrtc.try", "cpu": DEFAULT_CPU}) + properties = properties or {} + properties["builder_group"] = "tryserver.webrtc" + properties.update(make_goma_properties(enable_ats = goma_enable_ats, jobs = goma_jobs)) + if cq != None: + luci.cq_tryjob_verifier(name, cq_group = "cq", **cq) + if branch_cq: + luci.cq_tryjob_verifier(name, cq_group = "cq_branch", **cq) + + return webrtc_builder( + name = name, + dimensions = dimensions, + properties = properties, + bucket = "try", + service_account = "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com", + notifies = ["infra_failure_notifier"], + **kwargs + ) + +def perf_builder(name, perf_cat, **kwargs): + add_milo(name, {"perf": perf_cat}) + properties = make_goma_properties() + properties["builder_group"] = "client.webrtc.perf" + return webrtc_builder( + name = name, + dimensions = {"pool": "luci.webrtc.perf", "os": "Linux"}, + properties = properties, + bucket = "perf", + service_account = "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com", + # log_base of 1.7 means: + # when there are P pending builds, LUCI will batch the first B builds. + # P: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... + # B: 1 1 2 2 3 3 3 3 4 4 4 4 4 4 5 ... + triggering_policy = scheduler.logarithmic_batching(log_base = 1.7), + repo = WEBRTC_GIT, + execution_timeout = 3 * time.hour, + notifies = ["post_submit_failure_notifier", "infra_failure_notifier"], + **kwargs + ) + +def cron_builder(name, service_account = None, **kwargs): + if service_account == None: + service_account = "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + add_milo(name, {"cron": True}) + return webrtc_builder( + name = name, + dimensions = {"pool": "luci.webrtc.cron", "os": "Linux", "cpu": DEFAULT_CPU}, + bucket = "cron", + service_account = service_account, + notifies = ["cron_notifier"], + **kwargs + ) + +def normal_builder_factory(**common_kwargs): + def builder(*args, **kwargs): + kwargs.update(common_kwargs) + return ci_builder(*args, **kwargs) + + def try_job(name, **kwargs): + kwargs.update(common_kwargs) + return try_builder(name, **kwargs) + + return builder, try_job + +# Mixins: + +linux_builder, linux_try_job = normal_builder_factory( + dimensions = {"os": "Linux", "inside_docker": "0"}, +) + +android_builder, android_try_job = normal_builder_factory( + dimensions = {"os": "Linux"}, +) + +win_builder = normal_builder_factory( + dimensions = {"os": "Windows"}, +)[0] + +win_try_job = normal_builder_factory( + dimensions = {"os": "Windows"}, + goma_enable_ats = False, +)[1] + +mac_builder, mac_try_job = normal_builder_factory( + dimensions = {"os": "Mac"}, +) + +ios_builder, ios_try_job = normal_builder_factory( + dimensions = {"os": "Mac-11"}, + properties = {"xcode_build_version": WEBRTC_XCODE13}, + caches = [swarming.cache( + name = "xcode_ios_" + WEBRTC_XCODE13, + path = "xcode_ios_" + WEBRTC_XCODE13 + ".app", + )], +) + +# Actual builder configuration: + +android_builder("Android32 (M Nexus5X)(dbg)", "Android|arm|dbg") +android_try_job("android_compile_arm_dbg", cq = None) +android_try_job("android_arm_dbg") +android_builder("Android32 (M Nexus5X)", "Android|arm|rel") +android_try_job("android_arm_rel") +android_builder("Android32 Builder arm", "Android|arm|size", perf_cat = "Android|arm|Builder|", prioritized = True) +android_try_job("android_compile_arm_rel") +perf_builder("Perf Android32 (M Nexus5)", "Android|arm|Tester|M Nexus5", triggered_by = ["Android32 Builder arm"]) +perf_builder("Perf Android32 (M AOSP Nexus6)", "Android|arm|Tester|M AOSP Nexus6", triggered_by = ["Android32 Builder arm"]) +android_try_job("android_compile_arm64_dbg", cq = None) +android_try_job("android_arm64_dbg", cq = None) +android_builder("Android64 (M Nexus5X)", "Android|arm64|rel") +android_try_job("android_arm64_rel") +android_builder("Android64 Builder arm64", "Android|arm64|size", perf_cat = "Android|arm64|Builder|", prioritized = True) +perf_builder("Perf Android64 (M Nexus5X)", "Android|arm64|Tester|M Nexus5X", triggered_by = ["Android64 Builder arm64"]) +perf_builder("Perf Android64 (O Pixel2)", "Android|arm64|Tester|O Pixel2", triggered_by = ["Android64 Builder arm64"]) +android_try_job("android_compile_arm64_rel") +android_builder("Android64 Builder x64 (dbg)", "Android|x64|dbg") +android_try_job("android_compile_x64_dbg") +android_try_job("android_compile_x64_rel", cq = None) +android_builder("Android32 Builder x86 (dbg)", "Android|x86|dbg") +android_try_job("android_compile_x86_dbg") +android_builder("Android32 Builder x86", "Android|x86|rel") +android_try_job("android_compile_x86_rel") +android_builder("Android32 (more configs)", "Android|arm|more") +android_try_job("android_arm_more_configs") +android_try_job("android_chromium_compile", recipe = "chromium_trybot", branch_cq = False) + +ios_builder("iOS64 Debug", "iOS|arm64|dbg") +ios_try_job("ios_compile_arm64_dbg") +ios_builder("iOS64 Release", "iOS|arm64|rel") +ios_try_job("ios_compile_arm64_rel") +ios_builder("iOS64 Sim Debug (iOS 14.0)", "iOS|x64|14") +ios_try_job("ios_sim_x64_dbg_ios14") +ios_builder("iOS64 Sim Debug (iOS 13)", "iOS|x64|13") +ios_try_job("ios_sim_x64_dbg_ios13") +ios_builder("iOS64 Sim Debug (iOS 12)", "iOS|x64|12") +ios_try_job("ios_sim_x64_dbg_ios12") +ios_builder("iOS API Framework Builder", "iOS|fat|size", recipe = "ios_api_framework", prioritized = True) +ios_try_job("ios_api_framework", recipe = "ios_api_framework") + +linux_builder("Linux32 Debug", "Linux|x86|dbg") +linux_try_job("linux_x86_dbg") +linux_builder("Linux32 Release", "Linux|x86|rel") +linux_try_job("linux_x86_rel") +linux_builder("Linux64 Debug", "Linux|x64|dbg") +linux_try_job("linux_dbg", cq = None) +linux_try_job("linux_compile_dbg") +linux_builder("Linux64 Release", "Linux|x64|rel") +linux_try_job("linux_rel") +linux_builder("Linux64 Builder", "Linux|x64|size", perf_cat = "Linux|x64|Builder|", prioritized = True) +linux_try_job("linux_compile_rel") +perf_builder("Perf Linux Bionic", "Linux|x64|Tester|Bionic", triggered_by = ["Linux64 Builder"]) +linux_builder("Linux32 Debug (ARM)", "Linux|arm|dbg") +linux_try_job("linux_compile_arm_dbg") +linux_builder("Linux32 Release (ARM)", "Linux|arm|rel") +linux_try_job("linux_compile_arm_rel") +linux_builder("Linux64 Debug (ARM)", "Linux|arm64|dbg") +linux_try_job("linux_compile_arm64_dbg") +linux_builder("Linux64 Release (ARM)", "Linux|arm64|rel") +linux_try_job("linux_compile_arm64_rel") +linux_builder("Linux Asan", "Linux|x64|asan") +linux_try_job("linux_asan") +linux_builder("Linux MSan", "Linux|x64|msan") +linux_try_job("linux_msan") +linux_builder("Linux Tsan v2", "Linux|x64|tsan") +linux_try_job("linux_tsan2") +linux_builder("Linux UBSan", "Linux|x64|ubsan") +linux_try_job("linux_ubsan") +linux_builder("Linux UBSan vptr", "Linux|x64|ubsan") +linux_try_job("linux_ubsan_vptr") +linux_builder("Linux64 Release (Libfuzzer)", "Linux|x64|fuzz", recipe = "libfuzzer") +linux_try_job("linux_libfuzzer_rel", recipe = "libfuzzer") +linux_builder("Linux (more configs)", "Linux|x64|more") +linux_try_job("linux_more_configs") +linux_try_job("linux_chromium_compile", recipe = "chromium_trybot", branch_cq = False) +linux_try_job("linux_chromium_compile_dbg", recipe = "chromium_trybot", branch_cq = False) + +mac_builder("Mac64 Debug", "Mac|x64|dbg") +mac_try_job("mac_dbg", cq = None) +mac_try_job("mac_compile_dbg") +mac_builder("Mac64 Release", "Mac|x64|rel") +mac_try_job("mac_rel") +mac_try_job("mac_compile_rel", cq = None) +mac_builder("Mac64 Builder", ci_cat = None, perf_cat = "Mac|x64|Builder|") +mac_builder("MacArm64 Builder", ci_cat = None, perf_cat = "Mac|arm64|Builder") +perf_builder("Perf Mac 11", "Mac|x64|Tester|11", triggered_by = ["Mac64 Builder"]) +perf_builder("Perf Mac M1 Arm64 12", "Mac|arm64|Tester|12", triggered_by = ["MacArm64 Builder"]) + +mac_builder("Mac Asan", "Mac|x64|asan") +mac_try_job("mac_asan") +mac_try_job("mac_chromium_compile", recipe = "chromium_trybot", branch_cq = False) +mac_builder("MacARM64 M1 Release", "Mac|arm64M1|rel", cpu = "arm64-64-Apple_M1") +mac_try_job("mac_rel_m1", try_cat = None, cq = None) +mac_try_job("mac_dbg_m1", try_cat = None, cq = None) + +win_builder("Win32 Debug (Clang)", "Win Clang|x86|dbg") +win_try_job("win_x86_clang_dbg", cq = None) +win_try_job("win_compile_x86_clang_dbg") +win_builder("Win32 Release (Clang)", "Win Clang|x86|rel") +win_try_job("win_x86_clang_rel") +win_try_job("win_compile_x86_clang_rel", cq = None) +win_builder("Win32 Builder (Clang)", ci_cat = None, perf_cat = "Win|x86|Builder|") +perf_builder("Perf Win7", "Win|x86|Tester|7", triggered_by = ["Win32 Builder (Clang)"]) +win_builder("Win64 Debug (Clang)", "Win Clang|x64|dbg") +win_try_job("win_x64_clang_dbg", cq = None) +win_try_job("win_x64_clang_dbg_win10", cq = None) +win_try_job("win_compile_x64_clang_dbg") +win_builder("Win64 Release (Clang)", "Win Clang|x64|rel") +win_try_job("win_x64_clang_rel", cq = None) +win_try_job("win_compile_x64_clang_rel") +win_builder("Win64 ASan", "Win Clang|x64|asan") +win_try_job("win_asan") +win_builder("Win (more configs)", "Win Clang|x86|more") +win_try_job("win_x86_more_configs") +win_try_job("win_chromium_compile", recipe = "chromium_trybot", branch_cq = False, goma_jobs = 150) +win_try_job("win_chromium_compile_dbg", recipe = "chromium_trybot", branch_cq = False, goma_jobs = 150) + +linux_try_job( + "presubmit", + recipe = "run_presubmit", + properties = {"repo_name": "webrtc", "runhooks": True}, + priority = 28, + cq = {"disable_reuse": True}, +) + +cron_builder( + "Auto-roll - WebRTC DEPS", + recipe = "auto_roll_webrtc_deps", + schedule = "0 */2 * * *", # Every 2 hours. +) + +cron_builder( + "WebRTC version update", + recipe = "update_webrtc_binary_version", + schedule = "0 4 * * *", # Every day at 4am. + service_account = "webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com", +) + +lkgr_config = { + "project": "webrtc", + "source_url": WEBRTC_GIT, + "status_url": "https://webrtc-status.appspot.com", + "allowed_lag": 9, # hours (up to 10x during low commit volume periods) + "allowed_gap": 150, # commits behind + "error_recipients": WEBRTC_TROOPER_EMAIL, + "buckets": { + "webrtc/ci": { + # bucket alias: luci.webrtc.ci + "builders": [ + b + for b in sorted(lkgr_builders) + if b not in skipped_lkgr_bots + ], + }, + "chromium/webrtc.fyi": { + # bucket alias: luci.chromium.webrtc.fyi + "builders": [ + "WebRTC Chromium FYI Android Builder (dbg)", + "WebRTC Chromium FYI Android Builder ARM64 (dbg)", + "WebRTC Chromium FYI Android Builder", + "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)", + "WebRTC Chromium FYI Linux Builder (dbg)", + "WebRTC Chromium FYI Linux Builder", + "WebRTC Chromium FYI Linux Tester", + "WebRTC Chromium FYI Mac Builder (dbg)", + "WebRTC Chromium FYI Mac Builder", + "WebRTC Chromium FYI Mac Tester", + "WebRTC Chromium FYI Win Builder (dbg)", + "WebRTC Chromium FYI Win Builder", + "WebRTC Chromium FYI Win10 Tester", + "WebRTC Chromium FYI ios-device", + "WebRTC Chromium FYI ios-simulator", + ], + }, + }, +} + +cron_builder( + "WebRTC lkgr finder", + recipe = "lkgr_finder", + properties = { + "project": "webrtc", + "repo": WEBRTC_GIT, + "ref": "refs/heads/lkgr", + "src_ref": "refs/heads/main", + "lkgr_status_gs_path": "chromium-webrtc/lkgr-status", + "config": lkgr_config, + }, + schedule = "*/10 * * * *", # Every 10 minutes. +) diff --git a/infra/config/console-header.textpb b/infra/config/console-header.textpb new file mode 100644 index 0000000000..79e5d2bf21 --- /dev/null +++ b/infra/config/console-header.textpb @@ -0,0 +1,59 @@ +tree_status_host: "webrtc-status.appspot.com" +links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } +} +links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } +} +console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" +} diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg new file mode 100644 index 0000000000..68b8432b42 --- /dev/null +++ b/infra/config/cr-buildbucket.cfg @@ -0,0 +1,5908 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see BuildbucketCfg message: +# https://luci-config.appspot.com/schemas/projects:buildbucket.cfg + +buckets { + name: "ci" + acls { + group: "all" + } + acls { + role: SCHEDULER + group: "project-webrtc-ci-schedulers" + } + acls { + role: SCHEDULER + group: "service-account-chromeperf" + } + swarming { + builders { + name: "Android32 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 (M Nexus5X)(dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 Builder x86" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android32 Builder x86 (dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android64 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Android64 Builder x64 (dbg)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux Asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux MSan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux Tsan v2" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux UBSan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux UBSan vptr" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Debug (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux32 Release (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Debug (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release (ARM)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Release (Libfuzzer)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/libfuzzer"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac Asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "MacARM64 M1 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:arm64-64-Apple_M1" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win (more configs)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Debug (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Release (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 ASan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 Debug (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "Win64 Release (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS API Framework Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/ios_api_framework",' + ' "xcode_build_version": "13c100"' + '}' + priority: 29 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Debug" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Release" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 12)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 13)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + builders { + name: "iOS64 Sim Debug (iOS 14.0)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "ci_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "cron" + acls { + group: "all" + } + swarming { + builders { + name: "Auto-roll - WebRTC DEPS" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "recipe": "webrtc/auto_roll_webrtc_deps"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + builders { + name: "WebRTC lkgr finder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "config": {' + ' "allowed_gap": 150,' + ' "allowed_lag": 9,' + ' "buckets": {' + ' "chromium/webrtc.fyi": {' + ' "builders": [' + ' "WebRTC Chromium FYI Android Builder (dbg)",' + ' "WebRTC Chromium FYI Android Builder ARM64 (dbg)",' + ' "WebRTC Chromium FYI Android Builder",' + ' "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)",' + ' "WebRTC Chromium FYI Linux Builder (dbg)",' + ' "WebRTC Chromium FYI Linux Builder",' + ' "WebRTC Chromium FYI Linux Tester",' + ' "WebRTC Chromium FYI Mac Builder (dbg)",' + ' "WebRTC Chromium FYI Mac Builder",' + ' "WebRTC Chromium FYI Mac Tester",' + ' "WebRTC Chromium FYI Win Builder (dbg)",' + ' "WebRTC Chromium FYI Win Builder",' + ' "WebRTC Chromium FYI Win10 Tester",' + ' "WebRTC Chromium FYI ios-device",' + ' "WebRTC Chromium FYI ios-simulator"' + ' ]' + ' },' + ' "webrtc/ci": {' + ' "builders": [' + ' "Android32 (M Nexus5X)",' + ' "Android32 (M Nexus5X)(dbg)",' + ' "Android32 (more configs)",' + ' "Android32 Builder x86",' + ' "Android32 Builder x86 (dbg)",' + ' "Android64 (M Nexus5X)",' + ' "Android64 Builder x64 (dbg)",' + ' "Linux (more configs)",' + ' "Linux Asan",' + ' "Linux MSan",' + ' "Linux Tsan v2",' + ' "Linux UBSan",' + ' "Linux UBSan vptr",' + ' "Linux32 Debug",' + ' "Linux32 Debug (ARM)",' + ' "Linux32 Release",' + ' "Linux32 Release (ARM)",' + ' "Linux64 Debug",' + ' "Linux64 Debug (ARM)",' + ' "Linux64 Release",' + ' "Linux64 Release (ARM)",' + ' "Linux64 Release (Libfuzzer)",' + ' "Mac Asan",' + ' "Mac64 Debug",' + ' "Mac64 Release",' + ' "MacARM64 M1 Release",' + ' "Win (more configs)",' + ' "Win32 Debug (Clang)",' + ' "Win32 Release (Clang)",' + ' "Win64 ASan",' + ' "Win64 Debug (Clang)",' + ' "Win64 Release (Clang)",' + ' "iOS API Framework Builder",' + ' "iOS64 Debug",' + ' "iOS64 Release",' + ' "iOS64 Sim Debug (iOS 12)",' + ' "iOS64 Sim Debug (iOS 13)",' + ' "iOS64 Sim Debug (iOS 14.0)"' + ' ]' + ' }' + ' },' + ' "error_recipients": "webrtc-troopers-robots@google.com",' + ' "project": "webrtc",' + ' "source_url": "https://webrtc.googlesource.com/src",' + ' "status_url": "https://webrtc-status.appspot.com"' + ' },' + ' "lkgr_status_gs_path": "chromium-webrtc/lkgr-status",' + ' "project": "webrtc",' + ' "recipe": "lkgr_finder",' + ' "ref": "refs/heads/lkgr",' + ' "repo": "https://webrtc.googlesource.com/src",' + ' "src_ref": "refs/heads/main"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + builders { + name: "WebRTC version update" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.cron" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "recipe": "webrtc/update_webrtc_binary_version"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "cron_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "perf" + acls { + group: "all" + } + acls { + role: SCHEDULER + identity: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + swarming { + builders { + name: "Android32 Builder arm" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Android64 Builder arm64" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Linux64 Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 29 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Mac64 Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "MacArm64 Builder" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android32 (M AOSP Nexus6)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android32 (M Nexus5)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android64 (M Nexus5X)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Android64 (O Pixel2)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Linux Bionic" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Mac 11" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Mac M1 Arm64 12" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Perf Win7" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.perf" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc.perf",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + builders { + name: "Win32 Builder (Clang)" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "client.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "perf_test_results" + test_results {} + } + } + } + } +} +buckets { + name: "try" + acls { + group: "all" + } + acls { + role: SCHEDULER + group: "project-webrtc-tryjob-access" + } + acls { + role: SCHEDULER + group: "service-account-cq" + } + swarming { + builders { + name: "android_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x86_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "android_compile_x86_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_api_framework" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/ios_api_framework",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios12" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios13" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "ios_sim_x64_dbg_ios14" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone",' + ' "xcode_build_version": "13c100"' + '}' + priority: 30 + execution_timeout_secs: 7200 + caches { + name: "xcode_ios_13c100" + path: "xcode_ios_13c100.app" + } + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_chromium_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_arm_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_compile_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_libfuzzer_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/libfuzzer"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_msan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_tsan2" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_ubsan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_ubsan_vptr" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_x86_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "linux_x86_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_compile_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_dbg_m1" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "mac_rel_m1" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Mac" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "presubmit" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "inside_docker:0" + dimensions: "os:Linux" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "run_presubmit",' + ' "repo_name": "webrtc",' + ' "runhooks": true' + '}' + priority: 28 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_asan" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_chromium_compile" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "jobs": 150,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_chromium_compile_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "jobs": 150,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "chromium_trybot"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x64_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x64_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x86_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_compile_x86_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_dbg_win10" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x64_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_clang_dbg" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_clang_rel" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + builders { + name: "win_x86_more_configs" + swarming_host: "chromium-swarm.appspot.com" + swarming_tags: "vpython:native-python-wrapper" + dimensions: "cpu:x86-64" + dimensions: "os:Windows" + dimensions: "pool:luci.webrtc.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$build/goma": {' + ' "enable_ats": false,' + ' "server_host": "goma.chromium.org",' + ' "use_luci_auth": true' + ' },' + ' "$recipe_engine/isolated": {' + ' "server": "https://isolateserver.appspot.com"' + ' },' + ' "$recipe_engine/resultdb/test_presentation": {' + ' "column_keys": [],' + ' "grouping_keys": [' + ' "status",' + ' "v.test_suite"' + ' ]' + ' },' + ' "builder_group": "tryserver.webrtc",' + ' "recipe": "webrtc/standalone"' + '}' + priority: 30 + execution_timeout_secs: 7200 + build_numbers: YES + service_account: "webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + resultdb { + enable: true + bq_exports { + project: "webrtc-ci" + dataset: "resultdb" + table: "try_test_results" + test_results {} + } + } + } + } +} diff --git a/infra/config/generated/project.pyl b/infra/config/generated/project.pyl new file mode 100644 index 0000000000..df001db8e9 --- /dev/null +++ b/infra/config/generated/project.pyl @@ -0,0 +1,3 @@ +# This is a non-LUCI generated file +# This is consumed by infra/specs presubmit checks to validate the config +{"validate_source_side_specs_have_builder": False} diff --git a/infra/config/luci-logdog.cfg b/infra/config/luci-logdog.cfg new file mode 100644 index 0000000000..adc75bef49 --- /dev/null +++ b/infra/config/luci-logdog.cfg @@ -0,0 +1,9 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg + +reader_auth_groups: "all" +writer_auth_groups: "luci-logdog-chromium-writers" +archive_gs_bucket: "chromium-luci-logdog" diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg new file mode 100644 index 0000000000..3683845edb --- /dev/null +++ b/infra/config/luci-milo.cfg @@ -0,0 +1,611 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Project message: +# https://luci-config.appspot.com/schemas/projects:luci-milo.cfg + +consoles { + id: "ci" + name: "Main" + repo_url: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/master" + refs: "regexp:refs/heads/main" + manifest_name: "REVISION" + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (M Nexus5X)(dbg)" + category: "Android|arm" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (M Nexus5X)" + category: "Android|arm" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.perf/Android32 Builder arm" + category: "Android|arm" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 (M Nexus5X)" + category: "Android|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.perf/Android64 Builder arm64" + category: "Android|arm64" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android64 Builder x64 (dbg)" + category: "Android|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder x86 (dbg)" + category: "Android|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 Builder x86" + category: "Android|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Android32 (more configs)" + category: "Android|arm" + short_name: "more" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Debug" + category: "iOS|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Release" + category: "iOS|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 14.0)" + category: "iOS|x64" + short_name: "14" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 13)" + category: "iOS|x64" + short_name: "13" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS64 Sim Debug (iOS 12)" + category: "iOS|x64" + short_name: "12" + } + builders { + name: "buildbucket/luci.webrtc.ci/iOS API Framework Builder" + category: "iOS|fat" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Debug" + category: "Linux|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Release" + category: "Linux|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Debug" + category: "Linux|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release" + category: "Linux|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.perf/Linux64 Builder" + category: "Linux|x64" + short_name: "size" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Debug (ARM)" + category: "Linux|arm" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux32 Release (ARM)" + category: "Linux|arm" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Debug (ARM)" + category: "Linux|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release (ARM)" + category: "Linux|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux Asan" + category: "Linux|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux MSan" + category: "Linux|x64" + short_name: "msan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux Tsan v2" + category: "Linux|x64" + short_name: "tsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux UBSan" + category: "Linux|x64" + short_name: "ubsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux UBSan vptr" + category: "Linux|x64" + short_name: "ubsan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux64 Release (Libfuzzer)" + category: "Linux|x64" + short_name: "fuzz" + } + builders { + name: "buildbucket/luci.webrtc.ci/Linux (more configs)" + category: "Linux|x64" + short_name: "more" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac64 Debug" + category: "Mac|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac64 Release" + category: "Mac|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Mac Asan" + category: "Mac|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/MacARM64 M1 Release" + category: "Mac|arm64M1" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win32 Debug (Clang)" + category: "Win Clang|x86" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win32 Release (Clang)" + category: "Win Clang|x86" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 Debug (Clang)" + category: "Win Clang|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 Release (Clang)" + category: "Win Clang|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win64 ASan" + category: "Win Clang|x64" + short_name: "asan" + } + builders { + name: "buildbucket/luci.webrtc.ci/Win (more configs)" + category: "Win Clang|x86" + short_name: "more" + } + header { + links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } + } + links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } + } + console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" + } + tree_status_host: "webrtc-status.appspot.com" + } +} +consoles { + id: "perf" + name: "Perf" + repo_url: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/master" + refs: "regexp:refs/heads/main" + manifest_name: "REVISION" + builders { + name: "buildbucket/luci.webrtc.perf/Android32 Builder arm" + category: "Android|arm|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android32 (M Nexus5)" + category: "Android|arm|Tester" + short_name: "M Nexus5" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android32 (M AOSP Nexus6)" + category: "Android|arm|Tester" + short_name: "M AOSP Nexus6" + } + builders { + name: "buildbucket/luci.webrtc.perf/Android64 Builder arm64" + category: "Android|arm64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android64 (M Nexus5X)" + category: "Android|arm64|Tester" + short_name: "M Nexus5X" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Android64 (O Pixel2)" + category: "Android|arm64|Tester" + short_name: "O Pixel2" + } + builders { + name: "buildbucket/luci.webrtc.perf/Linux64 Builder" + category: "Linux|x64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Linux Bionic" + category: "Linux|x64|Tester" + short_name: "Bionic" + } + builders { + name: "buildbucket/luci.webrtc.perf/Mac64 Builder" + category: "Mac|x64|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/MacArm64 Builder" + category: "Mac|arm64" + short_name: "Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Mac 11" + category: "Mac|x64|Tester" + short_name: "11" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Mac M1 Arm64 12" + category: "Mac|arm64|Tester" + short_name: "12" + } + builders { + name: "buildbucket/luci.webrtc.perf/Win32 Builder (Clang)" + category: "Win|x86|Builder" + } + builders { + name: "buildbucket/luci.webrtc.perf/Perf Win7" + category: "Win|x86|Tester" + short_name: "7" + } + header { + links { + name: "Consoles" + links { + text: "WebRTC" + url: "/p/webrtc/g/ci" + alt: "WebRTC Main CI Console" + } + links { + text: "WebRTC Cron" + url: "/p/webrtc/g/cron" + alt: "WebRTC Cron Console" + } + links { + text: "WebRTC Perf" + url: "/p/webrtc/g/perf" + alt: "WebRTC Perf Console" + } + links { + text: "Chromium" + url: "/p/chromium/g/chromium.webrtc" + alt: "Chromium WebRTC Console" + } + links { + text: "Chromium FYI" + url: "/p/chromium/g/chromium.webrtc.fyi" + alt: "Chromium WebRTC FYI Console" + } + links { + text: "Try WebRTC" + url: "/p/webrtc/g/try" + alt: "WebRTC Try Builders" + } + } + links { + name: "Links" + links { + text: "Source" + url: "https://webrtc.googlesource.com/src/+/main/" + } + links { + text: "Reviews" + url: "https://webrtc-review.googlesource.com/" + } + links { + text: "Bugs" + url: "https://bugs.webrtc.org/" + } + links { + text: "LKGR status" + url: "https://storage.cloud.google.com/chromium-webrtc/lkgr-status/webrtc-lkgr-status.html" + } + } + console_groups { + console_ids: "webrtc/ci" + console_ids: "webrtc/perf" + console_ids: "chromium/chromium.webrtc" + console_ids: "chromium/chromium.webrtc.fyi" + } + tree_status_host: "webrtc-status.appspot.com" + } +} +consoles { + id: "cron" + name: "Cron" + builders { + name: "buildbucket/luci.webrtc.cron/Auto-roll - WebRTC DEPS" + } + builders { + name: "buildbucket/luci.webrtc.cron/WebRTC version update" + } + builders { + name: "buildbucket/luci.webrtc.cron/WebRTC lkgr finder" + } + builder_view_only: true +} +consoles { + id: "try" + name: "Tryserver" + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x86_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/android_compile_x86_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/android_arm_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/android_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios14" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios13" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_sim_x64_dbg_ios12" + } + builders { + name: "buildbucket/luci.webrtc.try/ios_api_framework" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_x86_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_x86_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm64_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_compile_arm64_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_msan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_tsan2" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_ubsan" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_ubsan_vptr" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_libfuzzer_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/linux_chromium_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_compile_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/mac_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x86_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x86_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_dbg_win10" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x64_clang_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x64_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_compile_x64_clang_rel" + } + builders { + name: "buildbucket/luci.webrtc.try/win_asan" + } + builders { + name: "buildbucket/luci.webrtc.try/win_x86_more_configs" + } + builders { + name: "buildbucket/luci.webrtc.try/win_chromium_compile" + } + builders { + name: "buildbucket/luci.webrtc.try/win_chromium_compile_dbg" + } + builders { + name: "buildbucket/luci.webrtc.try/presubmit" + } + builder_view_only: true +} +logo_url: "https://storage.googleapis.com/chrome-infra/webrtc-logo-vert-retro-255x305.png" diff --git a/infra/config/luci-notify.cfg b/infra/config/luci-notify.cfg new file mode 100644 index 0000000000..6a0e70b962 --- /dev/null +++ b/infra/config/luci-notify.cfg @@ -0,0 +1,2206 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-notify.cfg + +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (M Nexus5X)(dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 Builder x86" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android32 Builder x86 (dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android64 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Android64 Builder x64 (dbg)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux Asan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux MSan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux Tsan v2" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux UBSan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux UBSan vptr" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Debug (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux32 Release (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Debug (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release (ARM)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Linux64 Release (Libfuzzer)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac Asan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Mac64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "MacARM64 M1 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win (more configs)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win32 Debug (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win32 Release (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 ASan" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 Debug (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "Win64 Release (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS API Framework Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Debug" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Release" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 12)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 13)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "ci" + name: "iOS64 Sim Debug (iOS 14.0)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "Auto-roll - WebRTC DEPS" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "WebRTC lkgr finder" + } +} +notifiers { + notifications { + on_new_status: FAILURE + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "cron" + } + builders { + bucket: "cron" + name: "WebRTC version update" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Android32 Builder arm" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Android64 Builder arm64" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Linux64 Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Mac64 Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "MacArm64 Builder" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android32 (M AOSP Nexus6)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android32 (M Nexus5)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android64 (M Nexus5X)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Android64 (O Pixel2)" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Linux Bionic" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Mac 11" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Mac M1 Arm64 12" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Perf Win7" + repository: "https://webrtc.googlesource.com/src" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + notifications { + on_new_status: FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "build_failure" + notify_blamelist {} + } + builders { + bucket: "perf" + name: "Win32 Builder (Clang)" + repository: "https://webrtc.googlesource.com/src" + } + tree_closers { + tree_status_host: "webrtc-status.appspot.com" + failed_step_regexp: "bot_update|compile|gclient runhooks|runhooks|update|extract build|cleanup_temp|taskkill|compile|gn" + failed_step_regexp_exclude: ".*\\(experimental\\).*" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_more_configs" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x86_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "android_compile_x86_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_api_framework" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios12" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios13" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "ios_sim_x64_dbg_ios14" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_chromium_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm64_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm64_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_arm_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_compile_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_libfuzzer_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_more_configs" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_msan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_tsan2" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_ubsan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_ubsan_vptr" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_x86_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "linux_x86_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_compile_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_dbg_m1" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "mac_rel_m1" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "presubmit" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_asan" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_chromium_compile" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_chromium_compile_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x64_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x64_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x86_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_compile_x86_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_dbg_win10" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x64_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_clang_dbg" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_clang_rel" + } +} +notifiers { + notifications { + on_new_status: INFRA_FAILURE + email { + recipients: "webrtc-troopers-robots@google.com" + } + template: "infra_failure" + } + builders { + bucket: "try" + name: "win_x86_more_configs" + } +} +tree_closing_enabled: true diff --git a/infra/config/luci-notify/email-templates/build_failure.template b/infra/config/luci-notify/email-templates/build_failure.template new file mode 100644 index 0000000000..dc321cbc1f --- /dev/null +++ b/infra/config/luci-notify/email-templates/build_failure.template @@ -0,0 +1,24 @@ +Test failure in WebRTC on {{ .Build.Builder.Builder }} + +

There was a failure on builder "{{ .Build.Builder.Builder }}".

+ +

Full details are available here.

+ + + + + + + + + + + + + + + + + + +
New status:{{ .Build.Status }}
Previous status:{{ .OldStatus }}
Created at:{{ .Build.CreateTime | time }}
Finished at:{{ .Build.EndTime | time }}
diff --git a/infra/config/luci-notify/email-templates/cron.template b/infra/config/luci-notify/email-templates/cron.template new file mode 100644 index 0000000000..580b7414ad --- /dev/null +++ b/infra/config/luci-notify/email-templates/cron.template @@ -0,0 +1,33 @@ +{{ .Build.Builder.Builder }} failed to run + +

There was a failure on builder "{{ .Build.Builder.Builder }}".

+ +

Full details are available here.

+ + + + + + + + + + + + + + + + + + + + + + +
Builder: + + {{ .Build.Builder.Builder }} + + (Console) +
New status:{{ .Build.Status }}
Previous status:{{ .OldStatus }}
Created at:{{ .Build.CreateTime | time }}
Finished at:{{ .Build.EndTime | time }}
diff --git a/infra/config/luci-notify/email-templates/infra_failure.template b/infra/config/luci-notify/email-templates/infra_failure.template new file mode 100644 index 0000000000..5bedf6111a --- /dev/null +++ b/infra/config/luci-notify/email-templates/infra_failure.template @@ -0,0 +1,24 @@ +Infra failure in WebRTC on {{ .Build.Builder.Builder }} + +

There was a failure on builder "{{ .Build.Builder.Builder }}".

+ +

Full details are available here.

+ + + + + + + + + + + + + + + + + + +
New status:{{ .Build.Status }}
Previous status:{{ .OldStatus }}
Created at:{{ .Build.CreateTime | time }}
Finished at:{{ .Build.EndTime | time }}
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg new file mode 100644 index 0000000000..4670f07e11 --- /dev/null +++ b/infra/config/luci-scheduler.cfg @@ -0,0 +1,726 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-scheduler.cfg + +job { + id: "Android32 (M Nexus5X)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (M Nexus5X)" + } +} +job { + id: "Android32 (M Nexus5X)(dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (M Nexus5X)(dbg)" + } +} +job { + id: "Android32 (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 (more configs)" + } +} +job { + id: "Android32 Builder arm" + realm: "perf" + acl_sets: "perf" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Android32 Builder arm" + } +} +job { + id: "Android32 Builder x86" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 Builder x86" + } +} +job { + id: "Android32 Builder x86 (dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android32 Builder x86 (dbg)" + } +} +job { + id: "Android64 (M Nexus5X)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android64 (M Nexus5X)" + } +} +job { + id: "Android64 Builder arm64" + realm: "perf" + acl_sets: "perf" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Android64 Builder arm64" + } +} +job { + id: "Android64 Builder x64 (dbg)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Android64 Builder x64 (dbg)" + } +} +job { + id: "Auto-roll - WebRTC DEPS" + realm: "cron" + schedule: "0 */2 * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "Auto-roll - WebRTC DEPS" + } +} +job { + id: "Linux (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux (more configs)" + } +} +job { + id: "Linux Asan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux Asan" + } +} +job { + id: "Linux MSan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux MSan" + } +} +job { + id: "Linux Tsan v2" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux Tsan v2" + } +} +job { + id: "Linux UBSan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux UBSan" + } +} +job { + id: "Linux UBSan vptr" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux UBSan vptr" + } +} +job { + id: "Linux32 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Debug" + } +} +job { + id: "Linux32 Debug (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Debug (ARM)" + } +} +job { + id: "Linux32 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Release" + } +} +job { + id: "Linux32 Release (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux32 Release (ARM)" + } +} +job { + id: "Linux64 Builder" + realm: "perf" + acl_sets: "perf" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Linux64 Builder" + } +} +job { + id: "Linux64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Debug" + } +} +job { + id: "Linux64 Debug (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Debug (ARM)" + } +} +job { + id: "Linux64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release" + } +} +job { + id: "Linux64 Release (ARM)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release (ARM)" + } +} +job { + id: "Linux64 Release (Libfuzzer)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Linux64 Release (Libfuzzer)" + } +} +job { + id: "Mac Asan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac Asan" + } +} +job { + id: "Mac64 Builder" + realm: "perf" + acl_sets: "perf" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Mac64 Builder" + } +} +job { + id: "Mac64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac64 Debug" + } +} +job { + id: "Mac64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Mac64 Release" + } +} +job { + id: "MacARM64 M1 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "MacARM64 M1 Release" + } +} +job { + id: "MacArm64 Builder" + realm: "perf" + acl_sets: "perf" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "MacArm64 Builder" + } +} +job { + id: "Perf Android32 (M AOSP Nexus6)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android32 (M AOSP Nexus6)" + } +} +job { + id: "Perf Android32 (M Nexus5)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android32 (M Nexus5)" + } +} +job { + id: "Perf Android64 (M Nexus5X)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android64 (M Nexus5X)" + } +} +job { + id: "Perf Android64 (O Pixel2)" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Android64 (O Pixel2)" + } +} +job { + id: "Perf Linux Bionic" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Linux Bionic" + } +} +job { + id: "Perf Mac 11" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Mac 11" + } +} +job { + id: "Perf Mac M1 Arm64 12" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Mac M1 Arm64 12" + } +} +job { + id: "Perf Win7" + realm: "perf" + acls { + role: TRIGGERER + granted_to: "webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + acl_sets: "perf" + triggering_policy { + kind: LOGARITHMIC_BATCHING + log_base: 1.7 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Perf Win7" + } +} +job { + id: "WebRTC lkgr finder" + realm: "cron" + schedule: "*/10 * * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "WebRTC lkgr finder" + } +} +job { + id: "WebRTC version update" + realm: "cron" + schedule: "0 4 * * *" + acl_sets: "cron" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "cron" + builder: "WebRTC version update" + } +} +job { + id: "Win (more configs)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win (more configs)" + } +} +job { + id: "Win32 Builder (Clang)" + realm: "perf" + acl_sets: "perf" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "perf" + builder: "Win32 Builder (Clang)" + } +} +job { + id: "Win32 Debug (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win32 Debug (Clang)" + } +} +job { + id: "Win32 Release (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win32 Release (Clang)" + } +} +job { + id: "Win64 ASan" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 ASan" + } +} +job { + id: "Win64 Debug (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 Debug (Clang)" + } +} +job { + id: "Win64 Release (Clang)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "Win64 Release (Clang)" + } +} +job { + id: "iOS API Framework Builder" + realm: "ci" + acl_sets: "ci" + triggering_policy { + kind: GREEDY_BATCHING + max_concurrent_invocations: 3 + max_batch_size: 1 + } + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS API Framework Builder" + } +} +job { + id: "iOS64 Debug" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Debug" + } +} +job { + id: "iOS64 Release" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Release" + } +} +job { + id: "iOS64 Sim Debug (iOS 12)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 12)" + } +} +job { + id: "iOS64 Sim Debug (iOS 13)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 13)" + } +} +job { + id: "iOS64 Sim Debug (iOS 14.0)" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "iOS64 Sim Debug (iOS 14.0)" + } +} +trigger { + id: "webrtc-gitiles-trigger-main" + realm: "ci" + acl_sets: "ci" + triggers: "Android32 (M Nexus5X)" + triggers: "Android32 (M Nexus5X)(dbg)" + triggers: "Android32 (more configs)" + triggers: "Android32 Builder x86" + triggers: "Android32 Builder x86 (dbg)" + triggers: "Android64 (M Nexus5X)" + triggers: "Android64 Builder x64 (dbg)" + triggers: "Linux (more configs)" + triggers: "Linux Asan" + triggers: "Linux MSan" + triggers: "Linux Tsan v2" + triggers: "Linux UBSan" + triggers: "Linux UBSan vptr" + triggers: "Linux32 Debug" + triggers: "Linux32 Debug (ARM)" + triggers: "Linux32 Release" + triggers: "Linux32 Release (ARM)" + triggers: "Linux64 Debug" + triggers: "Linux64 Debug (ARM)" + triggers: "Linux64 Release" + triggers: "Linux64 Release (ARM)" + triggers: "Linux64 Release (Libfuzzer)" + triggers: "Mac Asan" + triggers: "Mac64 Debug" + triggers: "Mac64 Release" + triggers: "MacARM64 M1 Release" + triggers: "Win (more configs)" + triggers: "Win32 Debug (Clang)" + triggers: "Win32 Release (Clang)" + triggers: "Win64 ASan" + triggers: "Win64 Debug (Clang)" + triggers: "Win64 Release (Clang)" + triggers: "iOS API Framework Builder" + triggers: "iOS64 Debug" + triggers: "iOS64 Release" + triggers: "iOS64 Sim Debug (iOS 12)" + triggers: "iOS64 Sim Debug (iOS 13)" + triggers: "iOS64 Sim Debug (iOS 14.0)" + triggers: "Android32 Builder arm" + triggers: "Android64 Builder arm64" + triggers: "Linux64 Builder" + triggers: "Mac64 Builder" + triggers: "MacArm64 Builder" + triggers: "Win32 Builder (Clang)" + gitiles { + repo: "https://webrtc.googlesource.com/src" + refs: "regexp:refs/heads/main" + } +} +acl_sets { + name: "ci" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} +acl_sets { + name: "cron" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} +acl_sets { + name: "perf" + acls { + role: OWNER + granted_to: "group:project-webrtc-admins" + } + acls { + granted_to: "group:all" + } +} diff --git a/infra/config/project.cfg b/infra/config/project.cfg new file mode 100644 index 0000000000..692d4571a8 --- /dev/null +++ b/infra/config/project.cfg @@ -0,0 +1,15 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectCfg message: +# https://luci-config.appspot.com/schemas/projects:project.cfg + +name: "webrtc" +access: "group:all" +lucicfg { + version: "1.30.11" + package_dir: "." + config_dir: "." + entry_point: "config.star" + experiments: "crbug.com/1182002" +} diff --git a/infra/config/realms.cfg b/infra/config/realms.cfg new file mode 100644 index 0000000000..c88f5a147e --- /dev/null +++ b/infra/config/realms.cfg @@ -0,0 +1,179 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see RealmsCfg message: +# https://luci-config.appspot.com/schemas/projects:realms.cfg + +realms { + name: "@root" + bindings { + role: "role/buildbucket.reader" + principals: "group:all" + } + bindings { + role: "role/configs.reader" + principals: "group:all" + } + bindings { + role: "role/configs.validator" + principals: "user:webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/logdog.reader" + principals: "group:all" + } + bindings { + role: "role/logdog.writer" + principals: "group:luci-logdog-chromium-writers" + } + bindings { + role: "role/scheduler.owner" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/scheduler.reader" + principals: "group:all" + } + bindings { + role: "role/swarming.poolOwner" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-admins" + } + bindings { + role: "role/swarming.poolViewer" + principals: "group:all" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "user:chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-admins" + } +} +realms { + name: "ci" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-ci-schedulers" + principals: "group:service-account-chromeperf" + } + bindings { + role: "role/resultdb.invocationCreator" + principals: "group:project-webrtc-ci-task-accounts" + } +} +realms { + name: "cron" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com" + principals: "user:webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com" + } +} +realms { + name: "perf" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-led-users" + } + bindings { + role: "role/scheduler.triggerer" + principals: "user:webrtc-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + conditions { + restrict { + attribute: "scheduler.job.name" + values: "Perf Android32 (M AOSP Nexus6)" + values: "Perf Android32 (M Nexus5)" + values: "Perf Android64 (M Nexus5X)" + values: "Perf Android64 (O Pixel2)" + values: "Perf Linux Bionic" + values: "Perf Mac 11" + values: "Perf Mac M1 Arm64 12" + values: "Perf Win7" + } + } + } +} +realms { + name: "pools/ci" +} +realms { + name: "pools/ci-tests" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-ci-task-accounts" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "group:project-webrtc-ci-task-accounts" + } +} +realms { + name: "pools/cron" + bindings { + role: "role/swarming.poolUser" + principals: "project:libyuv" + } +} +realms { + name: "pools/perf" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-led-users" + } +} +realms { + name: "pools/try" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-led-users" + } +} +realms { + name: "pools/try-tests" + bindings { + role: "role/swarming.poolUser" + principals: "group:project-webrtc-try-task-accounts" + } + bindings { + role: "role/swarming.taskServiceAccount" + principals: "group:project-webrtc-try-task-accounts" + } +} +realms { + name: "try" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:webrtc-try-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-webrtc-tryjob-access" + principals: "group:service-account-cq" + } + bindings { + role: "role/resultdb.invocationCreator" + principals: "group:project-webrtc-try-task-accounts" + } + bindings { + role: "role/swarming.taskTriggerer" + principals: "group:project-webrtc-led-users" + } +} diff --git a/infra/specs/PRESUBMIT.py b/infra/specs/PRESUBMIT.py new file mode 100644 index 0000000000..df6966561a --- /dev/null +++ b/infra/specs/PRESUBMIT.py @@ -0,0 +1,66 @@ +# Copyright (c) 2022 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 os + + +def _HasLocalChanges(input_api): + ret = input_api.subprocess.call(['git', 'diff', '--quiet']) + return ret != 0 + + +def CheckPatchFormatted(input_api, output_api): + results = [] + file_filter = lambda x: x.LocalPath().endswith('.pyl') + affected_files = input_api.AffectedFiles(include_deletes=False, + file_filter=file_filter) + + for f in affected_files: + cmd = ['yapf', '-i', f.AbsoluteLocalPath()] + if input_api.subprocess.call(cmd): + results.append(output_api.PresubmitError('Error calling "' + cmd + '"')) + + if _HasLocalChanges(input_api): + msg = ('Diff found after running "yapf -i" on modified .pyl files.\n' + 'Please commit or discard the new changes.') + results.append(output_api.PresubmitError(msg)) + + return results + + +def CheckSourceSideSpecs(input_api, output_api): + d = os.path.dirname + angle_root = d(d(input_api.PresubmitLocalPath())) + gen_script = os.path.join(angle_root, 'testing', 'buildbot', + 'generate_buildbot_json.py') + + commands = [ + input_api.Command(name='generate_buildbot_json', + cmd=[ + input_api.python_executable, gen_script, '--check', + '--verbose', '--pyl-files-dir', + input_api.PresubmitLocalPath() + ], + kwargs={}, + message=output_api.PresubmitError), + ] + return input_api.RunTests(commands) + + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results.extend(CheckPatchFormatted(input_api, output_api)) + results.extend(CheckSourceSideSpecs(input_api, output_api)) + return results + + +def CheckChangeOnCommit(input_api, output_api): + results = [] + results.extend(CheckPatchFormatted(input_api, output_api)) + results.extend(CheckSourceSideSpecs(input_api, output_api)) + return results diff --git a/infra/specs/client.webrtc.json b/infra/specs/client.webrtc.json new file mode 100644 index 0000000000..64c5585833 --- /dev/null +++ b/infra/specs/client.webrtc.json @@ -0,0 +1,11883 @@ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "Android32 (M Nexus5X)": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "Android32 (M Nexus5X)(dbg)": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "Android32 (more configs)": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + }, + "Android32 Builder arm": {}, + "Android32 Builder x86": {}, + "Android32 Builder x86 (dbg)": {}, + "Android64 (M Nexus5X)": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "Android64 (M Nexus5X)(dbg)": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "Android64 Builder arm64": {}, + "Android64 Builder x64 (dbg)": {}, + "Linux (more configs)": { + "isolated_scripts": [ + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + }, + "Linux Asan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux MSan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux Tsan v2": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux UBSan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux UBSan vptr": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux32 Debug": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux32 Debug (ARM)": {}, + "Linux32 Release": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux32 Release (ARM)": {}, + "Linux64 Builder": {}, + "Linux64 Debug": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux64 Debug (ARM)": {}, + "Linux64 Release": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Linux64 Release (ARM)": {}, + "Linux64 Release (Libfuzzer)": {}, + "Mac Asan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Mac64 Builder": {}, + "Mac64 Debug": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Mac64 Release": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "MacARM64 M1 Release": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "MacArm64 Builder": {}, + "Win (more configs)": { + "isolated_scripts": [ + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + }, + "Win32 Builder (Clang)": {}, + "Win32 Debug (Clang)": {}, + "Win32 Release (Clang)": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows", + "pool": "WebRTC-baremetal" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Win64 ASan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "Win64 Debug (Clang)": {}, + "Win64 Release (Clang)": {}, + "iOS64 Debug": {}, + "iOS64 Release": {}, + "iOS64 Sim Debug (iOS 12)": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "iOS64 Sim Debug (iOS 13)": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "iOS64 Sim Debug (iOS 14.0)": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + } +} diff --git a/infra/specs/client.webrtc.perf.json b/infra/specs/client.webrtc.perf.json new file mode 100644 index 0000000000..e6f4e6aad6 --- /dev/null +++ b/infra/specs/client.webrtc.perf.json @@ -0,0 +1,594 @@ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "Perf Android32 (M AOSP Nexus6)": { + "gtest_tests": [ + { + "args": [ + ".", + "--remove", + "--android", + "--adb-path", + "../../third_party/android_sdk/public/platform-tools/adb", + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb" + ], + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "M", + "device_type": "shamu", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "low_bandwidth_audio_perf_test", + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb", + "--nologs" + ], + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "M", + "device_type": "shamu", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Android32 (M Nexus5)": { + "gtest_tests": [ + { + "args": [ + ".", + "--remove", + "--android", + "--adb-path", + "../../third_party/android_sdk/public/platform-tools/adb", + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb" + ], + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "M", + "device_type": "hammerhead", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "low_bandwidth_audio_perf_test", + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb", + "--nologs" + ], + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "M", + "device_type": "hammerhead", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Android64 (M Nexus5X)": { + "gtest_tests": [ + { + "args": [ + ".", + "--remove", + "--android", + "--adb-path", + "../../third_party/android_sdk/public/platform-tools/adb", + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb" + ], + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "low_bandwidth_audio_perf_test", + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb", + "--nologs" + ], + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Android64 (O Pixel2)": { + "gtest_tests": [ + { + "args": [ + ".", + "--remove", + "--android", + "--adb-path", + "../../third_party/android_sdk/public/platform-tools/adb", + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb" + ], + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_type": "walleye", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "low_bandwidth_audio_perf_test", + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb", + "--nologs" + ], + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "android_devices": "1", + "device_type": "walleye", + "os": "Android", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Linux Bionic": { + "isolated_scripts": [ + { + "args": [ + ".", + "--remove", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "low_bandwidth_audio_perf_test", + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "low_bandwidth_audio_perf_test", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-18.04", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--test_artifacts_dir=${ISOLATED_OUTDIR}", + "--save_worst_frame", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json", + "--nologs" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Ubuntu-18.04", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Mac 11": { + "isolated_scripts": [ + { + "args": [ + ".", + "--remove", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "low_bandwidth_audio_perf_test", + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "low_bandwidth_audio_perf_test", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--test_artifacts_dir=${ISOLATED_OUTDIR}", + "--save_worst_frame", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json", + "--nologs" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Mac M1 Arm64 12": { + "isolated_scripts": [ + { + "args": [ + ".", + "--remove", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "low_bandwidth_audio_perf_test", + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "low_bandwidth_audio_perf_test", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-12", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--test_artifacts_dir=${ISOLATED_OUTDIR}", + "--save_worst_frame", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json", + "--nologs" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-12", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "Perf Win7": { + "isolated_scripts": [ + { + "args": [ + ".", + "--remove", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "low_bandwidth_audio_perf_test", + "merge": { + "args": [ + "--test-suite", + "low_bandwidth_audio_perf_test" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "low_bandwidth_audio_perf_test", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Windows", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_perf_test/" + }, + { + "args": [ + "--test_artifacts_dir=${ISOLATED_OUTDIR}", + "--save_worst_frame", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json", + "--nologs" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "os": "Windows", + "pool": "WebRTC-perf" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800 + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + } +} diff --git a/infra/specs/generate_buildbot_json.py b/infra/specs/generate_buildbot_json.py new file mode 100755 index 0000000000..aa0375198f --- /dev/null +++ b/infra/specs/generate_buildbot_json.py @@ -0,0 +1,90 @@ +#!/usr/bin/env vpython3 +# Copyright (c) 2022 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. +"""Script to generate the test spec JSON files and the mixins.pyl file from the +ADDITIONAL_MIXINS dictonary. Calls Chromium's generate_buildbot_json. +""" + +import ast +import os +import subprocess +import sys + +_SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +_SRC_DIR = os.path.dirname(os.path.dirname(_SCRIPT_DIR)) +sys.path.insert(0, _SRC_DIR) +sys.path.insert(0, os.path.join(_SRC_DIR, 'testing', 'buildbot')) + +from testing.buildbot import generate_buildbot_json + +# Add custom mixins here. +WEBRTC_MIXIN_FILE_NAME = os.path.join(_SCRIPT_DIR, 'mixins_webrtc.pyl') +MIXIN_FILE_NAME = os.path.join(_SCRIPT_DIR, 'mixins.pyl') +MIXINS_PYL_TEMPLATE = """\ +# GENERATED FILE - DO NOT EDIT. +# Generated by {script_name} using data from +# {data_source} +# +# Copyright (c) 2022 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. + +{mixin_data} +""" + + +def generate_mixins_file_from_used_mixins(generator): + chromium_args = generate_buildbot_json.BBJSONGenerator.parse_args(argv=None) + chromium_generator = generate_buildbot_json.BBJSONGenerator(chromium_args) + chromium_generator.load_configuration_files() + + seen_mixins = set() + for waterfall in generator.waterfalls: + seen_mixins = seen_mixins.union(waterfall.get('mixins', set())) + for bot_name, tester in waterfall['machines'].items(): + del bot_name + seen_mixins = seen_mixins.union(tester.get('mixins', set())) + for suite in generator.test_suites.values(): + for test in suite.values(): + seen_mixins = seen_mixins.union(test.get('mixins', set())) + + found_mixins = ast.literal_eval(open(WEBRTC_MIXIN_FILE_NAME).read()) + for mixin in seen_mixins: + if mixin not in found_mixins: + found_mixins[mixin] = chromium_generator.mixins[mixin] + elif mixin in chromium_generator.mixins: + assert False, '"%s" is already defined in Chromium\'s mixins.pyl' % mixin + + format_data = { + 'script_name': os.path.basename(__file__), + 'data_source': 'mixins_webrtc.pyl and Chromium\'s mixins.pyl', + 'mixin_data': dict(sorted(found_mixins.items())), + } + with open(MIXIN_FILE_NAME, 'w') as f: + f.write(MIXINS_PYL_TEMPLATE.format(**format_data)) + + return subprocess.call(['yapf', '-i', MIXIN_FILE_NAME]) + + +def main(): + override_args = ['--pyl-files-dir', _SCRIPT_DIR] + webrtc_args = generate_buildbot_json.BBJSONGenerator.parse_args(override_args) + webrtc_generator = generate_buildbot_json.BBJSONGenerator(webrtc_args) + webrtc_generator.load_configuration_files() + webrtc_generator.resolve_configuration_files() + + generate_mixins_file_from_used_mixins(webrtc_generator) + return webrtc_generator.main() + + +if __name__ == '__main__': # pragma: no cover + sys.exit(main()) diff --git a/tools_webrtc/mb/gn_isolate_map.pyl b/infra/specs/gn_isolate_map.pyl similarity index 91% rename from tools_webrtc/mb/gn_isolate_map.pyl rename to infra/specs/gn_isolate_map.pyl index 526eecfebe..057e4fe96d 100644 --- a/tools_webrtc/mb/gn_isolate_map.pyl +++ b/infra/specs/gn_isolate_map.pyl @@ -1,4 +1,4 @@ -# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. +# Copyright (c) 2022 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 @@ -21,7 +21,7 @@ }, "AppRTCMobile_test_apk": { "label": "//examples:AppRTCMobile_test_apk", - "type": "additional_compile_target", + "type": "console_test_launcher", }, "android_junit_tests": { "label": "//:android_junit_tests", @@ -37,7 +37,7 @@ }, "apprtcmobile_tests": { "label": "//examples:apprtcmobile_tests", - "type": "raw", + "type": "console_test_launcher", }, "audio_decoder_unittests": { "label": "//modules/audio_coding:audio_decoder_unittests", @@ -57,22 +57,16 @@ }, "android_instrumentation_test_apk": { "label": "//sdk/android:android_instrumentation_test_apk", - "type": "additional_compile_target", + "type": "console_test_launcher", }, "low_bandwidth_audio_test": { "label": "//audio:low_bandwidth_audio_test", "type": "console_test_launcher", - "args": [ - "--quick", - ], }, "low_bandwidth_audio_perf_test": { "label": "//audio:low_bandwidth_audio_perf_test", "type": "script", "script": "//audio/test/low_bandwidth_audio_test.py", - "args": [ - ".", "--remove", - ], }, "modules_tests": { "label": "//modules:modules_tests", @@ -104,11 +98,15 @@ }, "sdk_framework_unittests": { "label": "//sdk:sdk_framework_unittests", - "type": "raw", + "type": "console_test_launcher", }, "sdk_unittests": { "label": "//sdk:sdk_unittests", - "type": "raw", + "type": "console_test_launcher", + }, + "slow_peer_connection_unittests": { + "label": "//pc:slow_peer_connection_unittests", + "type": "console_test_launcher", }, "system_wrappers_unittests": { "label": "//system_wrappers:system_wrappers_unittests", diff --git a/infra/specs/internal.client.webrtc.json b/infra/specs/internal.client.webrtc.json new file mode 100644 index 0000000000..7ef9550fd5 --- /dev/null +++ b/infra/specs/internal.client.webrtc.json @@ -0,0 +1,1042 @@ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "iOS64 Debug": { + "isolated_scripts": [ + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--readline-timeout=1200", + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "hard_timeout": 7200, + "io_timeout": 7200, + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + } + ] + }, + "iOS64 Perf": { + "isolated_scripts": [ + { + "args": [ + "--write_perf_output_on_ios", + "--nologs", + "--xcode-build-version", + "12d4e", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--nologs" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [ + "--test-suite", + "webrtc_perf_tests" + ], + "script": "//tools_webrtc/perf/process_perf_results_py2.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "id": "build15-a7", + "os": "iOS-12.4.1", + "pool": "WebRTC" + } + ], + "hard_timeout": 10800, + "idempotent": false, + "io_timeout": 10800, + "named_caches": [ + { + "name": "xcode_ios_12d4e", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "iOS64 Release": { + "isolated_scripts": [ + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--readline-timeout=1200", + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "hard_timeout": 7200, + "io_timeout": 7200, + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--xctest", + "--undefok=enable-run-ios-unittests-with-xctest", + "--xcode-build-version", + "13c100", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "iOS-15.3", + "pool": "chrome.tests" + } + ], + "named_caches": [ + { + "name": "xcode_ios_13c100", + "path": "Xcode.app" + } + ], + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + } + ] + } +} diff --git a/infra/specs/mixins.pyl b/infra/specs/mixins.pyl new file mode 100644 index 0000000000..525688c08a --- /dev/null +++ b/infra/specs/mixins.pyl @@ -0,0 +1,362 @@ +# GENERATED FILE - DO NOT EDIT. +# Generated by generate_buildbot_json.py using data from +# mixins_webrtc.pyl and Chromium's mixins.pyl +# +# Copyright (c) 2022 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. + +{ + 'android-devices': { + 'swarming': { + 'dimensions': { + 'android_devices': '1' + } + } + }, + 'baremetal-pool': { + 'swarming': { + 'dimensions': { + 'pool': 'WebRTC-baremetal' + } + } + }, + 'baremetal-try-pool': { + 'swarming': { + 'dimensions': { + 'pool': 'WebRTC-baremetal-try' + } + } + }, + 'bullhead': { + 'swarming': { + 'dimensions': { + 'device_type': 'bullhead', + 'os': 'Android' + } + } + }, + 'chrome-tester-service-account': { + 'swarming': { + 'service_account': + 'chrome-tester@chops-service-accounts.iam.gserviceaccount.com' + } + }, + 'chromium-tester-service-account': { + 'swarming': { + 'service_account': + 'chromium-tester@chops-service-accounts.iam.gserviceaccount.com' + } + }, + 'cores-12': { + 'swarming': { + 'dimensions': { + 'cores': '12' + } + } + }, + 'hammerhead': { + 'swarming': { + 'dimensions': { + 'device_type': 'hammerhead', + 'os': 'Android' + } + } + }, + 'has_native_resultdb_integration': { + 'resultdb': { + 'enable': True, + 'has_native_resultdb_integration': True + } + }, + 'ios-device-15.3': { + 'swarming': { + 'dimensions': { + 'os': 'iOS-15.3', + 'pool': 'chrome.tests' + } + } + }, + 'ios-device-perf': { + 'swarming': { + 'idempotent': False, + 'dimensions': { + 'os': 'iOS-12.4.1', + 'pool': 'WebRTC', + 'id': 'build15-a7' + } + } + }, + 'ios-simulator-12.4': { + '$mixin_append': { + 'args': ['--platform', 'iPhone X', '--version', '12.4'], + 'swarming': { + 'named_caches': [{ + 'name': 'runtime_ios_12_4', + 'path': 'Runtime-ios-12.4' + }] + } + } + }, + 'ios-simulator-13.6': { + '$mixin_append': { + 'args': ['--platform', 'iPhone X', '--version', '13.6'], + 'swarming': { + 'named_caches': [{ + 'name': 'runtime_ios_13_6', + 'path': 'Runtime-ios-13.6' + }] + } + } + }, + 'ios-simulator-14.0': { + '$mixin_append': { + 'args': ['--platform', 'iPhone X', '--version', '14.0'], + 'swarming': { + 'named_caches': [{ + 'name': 'runtime_ios_14_0', + 'path': 'Runtime-ios-14.0' + }] + } + } + }, + 'linux': { + 'swarming': { + 'dimensions': { + 'os': 'Ubuntu' + } + } + }, + 'linux-bionic': { + 'swarming': { + 'dimensions': { + 'os': 'Ubuntu-18.04' + } + } + }, + 'logdog-butler': { + 'swarming': { + 'cipd_packages': [{ + 'cipd_package': + 'infra/tools/luci/logdog/butler/${platform}', + 'location': + 'bin', + 'revision': + 'git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c' + }] + } + }, + 'mac': { + 'swarming': { + 'dimensions': { + 'os': 'Mac' + } + } + }, + 'mac-m1-cpu': { + 'swarming': { + 'dimensions': { + 'cpu': 'arm64-64-Apple_M1' + } + } + }, + 'mac11': { + 'swarming': { + 'dimensions': { + 'os': 'Mac-11' + } + } + }, + 'mac_12_beta_arm64': { + 'swarming': { + 'dimensions': { + 'cpu': 'arm64', + 'os': 'Mac-12' + } + } + }, + 'mac_toolchain': { + 'swarming': { + 'cipd_packages': [{ + 'cipd_package': + 'infra/tools/mac_toolchain/${platform}', + 'location': + '.', + 'revision': + 'git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1' + }] + } + }, + 'marshmallow': { + 'swarming': { + 'dimensions': { + 'device_os': 'MMB29Q' + } + } + }, + 'marshmallow_generic': { + 'swarming': { + 'dimensions': { + 'device_os': 'M' + } + } + }, + 'out_dir_arg': { + '$mixin_append': { + 'args': ['--out-dir', '${ISOLATED_OUTDIR}'] + } + }, + 'perf-low-bandwidth-audio-perf-test': { + 'merge': { + 'script': '//tools_webrtc/perf/process_perf_results_py2.py', + 'args': ['--test-suite', 'low_bandwidth_audio_perf_test'] + } + }, + 'perf-output': { + '$mixin_append': { + 'args': [ + '--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb' + ] + } + }, + 'perf-pool': { + 'swarming': { + 'idempotent': False, + 'dimensions': { + 'pool': 'WebRTC-perf' + } + } + }, + 'perf-webrtc-perf-tests': { + 'merge': { + 'script': '//tools_webrtc/perf/process_perf_results_py2.py', + 'args': ['--test-suite', 'webrtc_perf_tests'] + }, + '$mixin_append': { + 'args': ['--nologs'] + } + }, + 'quick-perf-tests': { + '$mixin_append': { + 'args': + ['--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/', '--nologs'] + } + }, + 'resultdb-gtest-json-format': { + '$mixin_append': { + 'args': ['--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json'] + }, + 'resultdb': { + 'result_format': 'gtest_json', + 'result_file': '${ISOLATED_OUTDIR}/gtest_output.json' + } + }, + 'resultdb-json-format': { + 'resultdb': { + 'result_format': 'json' + } + }, + 'shamu': { + 'swarming': { + 'dimensions': { + 'device_type': 'shamu', + 'os': 'Android' + } + } + }, + 'timeout-2h': { + 'swarming': { + 'hard_timeout': 7200, + 'io_timeout': 7200 + } + }, + 'timeout-3h': { + 'swarming': { + 'hard_timeout': 10800, + 'io_timeout': 10800 + } + }, + 'walleye': { + 'swarming': { + 'dimensions': { + 'device_type': 'walleye', + 'os': 'Android' + } + } + }, + 'webrtc-xctest': { + '$mixin_append': { + 'args': ['--xctest', '--undefok=enable-run-ios-unittests-with-xctest'] + } + }, + 'win': { + 'swarming': { + 'dimensions': { + 'os': 'Windows' + } + } + }, + 'win10-1703': { + 'swarming': { + 'dimensions': { + 'os': 'Windows-10-15063' + } + } + }, + 'win7': { + 'swarming': { + 'dimensions': { + 'os': 'Windows-7-SP1' + } + } + }, + 'x86-64': { + 'swarming': { + 'dimensions': { + 'cpu': 'x86-64' + } + } + }, + 'xcode_12a7209': { + '$mixin_append': { + 'args': ['--xcode-build-version', '12a7209'], + 'swarming': { + 'named_caches': [{ + 'name': 'xcode_ios_12a7209', + 'path': 'Xcode.app' + }] + } + } + }, + 'xcode_12d4e': { + '$mixin_append': { + 'args': ['--xcode-build-version', '12d4e'], + 'swarming': { + 'named_caches': [{ + 'name': 'xcode_ios_12d4e', + 'path': 'Xcode.app' + }] + } + } + }, + 'xcode_13_main': { + '$mixin_append': { + 'args': ['--xcode-build-version', '13c100'] + }, + 'swarming': { + 'named_caches': [{ + 'name': 'xcode_ios_13c100', + 'path': 'Xcode.app' + }] + } + }, + 'xcode_parallelization': { + '$mixin_append': { + 'args': ['--xcode-parallelization'] + } + } +} diff --git a/infra/specs/mixins_webrtc.pyl b/infra/specs/mixins_webrtc.pyl new file mode 100644 index 0000000000..287c69c659 --- /dev/null +++ b/infra/specs/mixins_webrtc.pyl @@ -0,0 +1,261 @@ +# Copyright (c) 2022 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. + +{ + 'android-devices': { + 'swarming': { + 'dimensions': { + 'android_devices': '1', + }, + }, + }, + 'baremetal-pool': { + 'swarming': { + 'dimensions': { + 'pool': 'WebRTC-baremetal', + }, + }, + }, + 'baremetal-try-pool': { + 'swarming': { + 'dimensions': { + 'pool': 'WebRTC-baremetal-try', + }, + }, + }, + 'cores-12': { + 'swarming': { + 'dimensions': { + 'cores': '12', + } + } + }, + 'ios-device-15.3': { + 'swarming': { + 'dimensions': { + 'os': 'iOS-15.3', + 'pool': 'chrome.tests', + }, + }, + }, + 'ios-device-perf': { + 'swarming': { + 'idempotent': False, + 'dimensions': { + 'os': 'iOS-12.4.1', + 'pool': 'WebRTC', + 'id': 'build15-a7', + }, + }, + }, + 'ios-simulator-12.4': { + '$mixin_append': { + 'args': [ + '--platform', + 'iPhone X', + '--version', + '12.4', + ], + 'swarming': { + 'named_caches': [ + { + 'name': 'runtime_ios_12_4', + 'path': 'Runtime-ios-12.4', + }, + ], + }, + } + }, + 'ios-simulator-13.6': { + '$mixin_append': { + 'args': [ + '--platform', + 'iPhone X', + '--version', + '13.6', + ], + 'swarming': { + 'named_caches': [ + { + 'name': 'runtime_ios_13_6', + 'path': 'Runtime-ios-13.6', + }, + ], + }, + } + }, + 'ios-simulator-14.0': { + '$mixin_append': { + 'args': [ + '--platform', + 'iPhone X', + '--version', + '14.0', + ], + 'swarming': { + 'named_caches': [ + { + 'name': 'runtime_ios_14_0', + 'path': 'Runtime-ios-14.0', + }, + ], + }, + } + }, + 'quick-perf-tests': { + '$mixin_append': { + 'args': [ + '--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/', + '--nologs', + ], + } + }, + 'linux': { + 'swarming': { + 'dimensions': { + 'os': 'Ubuntu' + } + } + }, + 'logdog-butler': { + 'swarming': { + 'cipd_packages': [ + { + "cipd_package": 'infra/tools/luci/logdog/butler/${platform}', + 'location': 'bin', + 'revision': 'git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c', + }, + ], + }, + }, + 'mac': { + 'swarming': { + 'dimensions': { + 'os': 'Mac', + } + } + }, + 'mac-m1-cpu': { + 'swarming': { + 'dimensions': { + 'cpu': 'arm64-64-Apple_M1', + } + } + }, + 'mac11': { + 'swarming': { + 'dimensions': { + 'os': 'Mac-11' + } + } + }, + 'perf-low-bandwidth-audio-perf-test': { + 'merge': { + 'script': '//tools_webrtc/perf/process_perf_results_py2.py', + 'args': ['--test-suite', 'low_bandwidth_audio_perf_test'], + }, + }, + 'perf-output': { + '$mixin_append': { + 'args': [ + '--isolated-script-test-perf-output=${ISOLATED_OUTDIR}/perftest-output.pb', + ], + }, + }, + 'perf-pool': { + 'swarming': { + # Perf tests are marked as not idempotent, which means they're re-run even + # if they did not change this build. This will give the dashboard some + # more variance data to work with. + 'idempotent': False, + 'dimensions': { + 'pool': 'WebRTC-perf', + }, + }, + }, + 'perf-webrtc-perf-tests': { + 'merge': { + 'script': '//tools_webrtc/perf/process_perf_results_py2.py', + 'args': ['--test-suite', 'webrtc_perf_tests'], + }, + '$mixin_append': { + 'args': ['--nologs'] + } + }, + 'resultdb-gtest-json-format': { + '$mixin_append': { + 'args': [ + '--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json', + ], + }, + 'resultdb': { + 'result_format': 'gtest_json', + 'result_file': '${ISOLATED_OUTDIR}/gtest_output.json', + }, + }, + 'resultdb-json-format': { + 'resultdb': { + 'result_format': 'json' + } + }, + 'shamu': { + 'swarming': { + 'dimensions': { + 'device_type': 'shamu', + 'os': 'Android', + }, + }, + }, + 'timeout-2h': { + 'swarming': { + 'hard_timeout': 7200, + 'io_timeout': 7200, + }, + }, + 'timeout-3h': { + 'swarming': { + 'hard_timeout': 10800, + 'io_timeout': 10800, + }, + }, + 'webrtc-xctest': { + '$mixin_append': { + 'args': [ + '--xctest', + '--undefok=enable-run-ios-unittests-with-xctest', + ], + }, + }, + 'win': { + 'swarming': { + 'dimensions': { + 'os': 'Windows' + } + } + }, + 'win10-1703': { + 'swarming': { + 'dimensions': { + 'os': 'Windows-10-15063', + }, + }, + }, + 'xcode_12a7209': { + '$mixin_append': { + 'args': ['--xcode-build-version', '12a7209'], + 'swarming': { + 'named_caches': [ + { + 'name': 'xcode_ios_12a7209', + 'path': 'Xcode.app', + }, + ], + }, + }, + }, +} diff --git a/infra/specs/setup.cfg b/infra/specs/setup.cfg new file mode 100644 index 0000000000..7dd0a8a68e --- /dev/null +++ b/infra/specs/setup.cfg @@ -0,0 +1,11 @@ +# Copyright (c) 2022 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 is the style settings used when running yapf on .pyl files. +[yapf] +continuation_indent_width = 2 \ No newline at end of file diff --git a/infra/specs/test_suite_exceptions.pyl b/infra/specs/test_suite_exceptions.pyl new file mode 100644 index 0000000000..c31ab89d2e --- /dev/null +++ b/infra/specs/test_suite_exceptions.pyl @@ -0,0 +1,9 @@ +# Copyright (c) 2022 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. + +{} \ No newline at end of file diff --git a/infra/specs/test_suites.pyl b/infra/specs/test_suites.pyl new file mode 100644 index 0000000000..92de58d12e --- /dev/null +++ b/infra/specs/test_suites.pyl @@ -0,0 +1,295 @@ +# Copyright (c) 2022 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. + +{ + 'basic_suites': { + 'android_junit_tests': { + 'android_examples_junit_tests': {}, + 'android_sdk_junit_tests': {}, + }, + 'android_perf_tests': { + 'low_bandwidth_audio_perf_test': { + 'mixins': ['perf-low-bandwidth-audio-perf-test'], + 'args': [ + '.', + '--remove', + '--android', + '--adb-path', + '../../third_party/android_sdk/public/platform-tools/adb', + ] + }, + 'webrtc_perf_tests': { + 'mixins': ['perf-webrtc-perf-tests'], + }, + }, + 'android_tests': { + 'AppRTCMobile_test_apk': {}, + 'android_instrumentation_test_apk': {}, + 'audio_decoder_unittests': {}, + 'common_audio_unittests': {}, + 'common_video_unittests': {}, + 'dcsctp_unittests': {}, + 'modules_tests': { + 'swarming': { + 'shards': 2 + }, + }, + 'modules_unittests': { + 'swarming': { + 'shards': 6 + }, + }, + 'peerconnection_unittests': { + 'swarming': { + 'shards': 4 + }, + }, + 'rtc_media_unittests': {}, + 'rtc_pc_unittests': {}, + 'rtc_stats_unittests': {}, + 'rtc_unittests': { + 'swarming': { + 'shards': 6 + }, + }, + 'slow_peer_connection_unittests': {}, + 'system_wrappers_unittests': {}, + 'test_support_unittests': {}, + 'tools_unittests': {}, + 'video_engine_tests': { + 'swarming': { + 'shards': 4 + }, + }, + 'voip_unittests': {}, + 'webrtc_nonparallel_tests': {}, + }, + 'android_webrtc_perf_tests_tryserver': { + 'webrtc_perf_tests': { + 'mixins': ['quick-perf-tests'], + } + }, + 'desktop_perf_tests': { + 'low_bandwidth_audio_perf_test': { + 'mixins': ['perf-low-bandwidth-audio-perf-test'], + 'args': ['.', '--remove'], + }, + 'webrtc_perf_tests': { + 'mixins': ['perf-webrtc-perf-tests'], + 'args': [ + '--test_artifacts_dir=${ISOLATED_OUTDIR}', + '--save_worst_frame', + ], + }, + }, + 'desktop_tests': { + 'audio_decoder_unittests': {}, + 'common_audio_unittests': {}, + 'common_video_unittests': {}, + 'dcsctp_unittests': {}, + 'low_bandwidth_audio_test': { + 'args': ['--quick'] + }, + 'modules_tests': { + 'swarming': { + 'shards': 2 + }, + }, + 'modules_unittests': { + 'swarming': { + 'shards': 6 + }, + }, + 'peerconnection_unittests': { + 'swarming': { + 'shards': 4 + }, + }, + 'rtc_media_unittests': {}, + 'rtc_pc_unittests': {}, + 'rtc_stats_unittests': {}, + 'rtc_unittests': { + 'swarming': { + 'shards': 6 + }, + }, + 'slow_peer_connection_unittests': {}, + 'system_wrappers_unittests': {}, + 'test_support_unittests': {}, + 'tools_unittests': {}, + 'video_engine_tests': { + 'swarming': { + 'shards': 4 + }, + }, + 'voip_unittests': {}, + 'webrtc_nonparallel_tests': {}, + }, + 'ios_device_tests': { + # TODO(bugs.webrtc.org/11362): Real XCTests fail to start on devices. + #'apprtcmobile_tests': {'mixins': ['xcodebuild-device-runner']}, + 'common_audio_unittests': {}, + 'common_video_unittests': {}, + 'modules_tests': { + 'mixins': ['timeout-2h'], + 'args': [ + # Some tests exceed the default 180 seconds readline timeout. + '--readline-timeout=1200', + ] + }, + 'modules_unittests': {}, + 'rtc_pc_unittests': {}, + 'rtc_stats_unittests': {}, + # TODO(bugs.webrtc.org/11362): Real XCTests fail to start on devices. + #'sdk_framework_unittests': {'mixins': ['xcodebuild-device-runner']}, + #'sdk_unittests': {'mixins': ['xcodebuild-device-runner']}, + 'system_wrappers_unittests': {}, + 'test_support_unittests': {}, + 'tools_unittests': {}, + 'video_capture_tests': {}, + 'video_engine_tests': {}, + }, + 'ios_perf_tests': { + 'webrtc_perf_tests': { + 'mixins': ['perf-webrtc-perf-tests'], + 'args': ['--write_perf_output_on_ios', '--nologs'] + }, + }, + 'ios_simulator_tests': { + 'apprtcmobile_tests': { + 'mixins': ['xcode_parallelization'] + }, + 'audio_decoder_unittests': {}, + 'common_audio_unittests': {}, + 'common_video_unittests': {}, + 'dcsctp_unittests': {}, + 'modules_tests': {}, + 'modules_unittests': {}, + 'rtc_media_unittests': {}, + 'rtc_pc_unittests': {}, + 'rtc_stats_unittests': {}, + 'rtc_unittests': {}, + 'sdk_framework_unittests': { + 'mixins': ['xcode_parallelization'] + }, + 'sdk_unittests': { + 'mixins': ['xcode_parallelization'] + }, + 'system_wrappers_unittests': {}, + 'test_support_unittests': {}, + 'tools_unittests': {}, + 'video_capture_tests': {}, + 'video_engine_tests': {}, + 'voip_unittests': {}, + 'webrtc_nonparallel_tests': {}, + }, + 'linux_video_capture_tests': { + 'video_capture_tests': { + 'remove_mixins': ['linux-bionic'], + 'mixins': ['linux', 'baremetal-pool'], + } + }, + 'linux_video_capture_tests_tryserver': { + 'video_capture_tests': { + 'remove_mixins': ['linux-bionic'], + 'mixins': ['linux', 'baremetal-try-pool'], + } + }, + 'linux_webrtc_perf_tests_tryserver': { + 'webrtc_perf_tests': { + 'remove_mixins': ['linux-bionic', 'resultdb-json-format'], + 'mixins': [ + 'linux', 'baremetal-try-pool', 'quick-perf-tests', + 'resultdb-gtest-json-format' + ], + } + }, + 'mac_video_capture_tests': { + 'video_capture_tests': { + 'remove_mixins': ['mac11'], + 'mixins': ['mac', 'baremetal-pool'], + } + }, + 'mac_video_capture_tests_tryserver': { + 'video_capture_tests': { + 'remove_mixins': ['mac11'], + 'mixins': ['mac', 'baremetal-try-pool'], + } + }, + 'mac_webrtc_perf_tests_tryserver': { + 'webrtc_perf_tests': { + 'remove_mixins': ['mac11', 'resultdb-json-format'], + 'mixins': [ + 'mac', 'baremetal-try-pool', 'quick-perf-tests', + 'resultdb-gtest-json-format' + ], + } + }, + 'more_configs_tests': { + 'peerconnection_unittests': { + 'swarming': { + 'shards': 4 + }, + }, + }, + 'win_video_capture_tests': { + 'video_capture_tests': { + 'remove_mixins': ['win7'], + 'mixins': ['win', 'baremetal-pool'], + } + }, + 'win_video_capture_tests_tryserver': { + 'video_capture_tests': { + 'remove_mixins': ['win7'], + 'mixins': ['win', 'baremetal-try-pool'], + } + }, + 'win_webrtc_perf_tests_tryserver': { + 'webrtc_perf_tests': { + 'remove_mixins': ['win7', 'resultdb-json-format'], + 'mixins': [ + 'win', 'baremetal-try-pool', 'quick-perf-tests', + 'resultdb-gtest-json-format' + ], + } + }, + }, + 'compound_suites': { + 'android_tests_tryserver': [ + 'android_tests', + 'android_webrtc_perf_tests_tryserver', + ], + 'linux_tests': [ + 'desktop_tests', + 'linux_video_capture_tests', + ], + 'linux_tests_tryserver': [ + 'desktop_tests', + 'linux_video_capture_tests_tryserver', + 'linux_webrtc_perf_tests_tryserver', + ], + 'mac_tests': [ + 'desktop_tests', + 'mac_video_capture_tests', + ], + 'mac_tests_tryserver': [ + 'desktop_tests', + 'mac_video_capture_tests_tryserver', + 'mac_webrtc_perf_tests_tryserver', + ], + 'win_tests': [ + 'desktop_tests', + 'win_video_capture_tests', + ], + 'win_tests_tryserver': [ + 'desktop_tests', + 'win_video_capture_tests_tryserver', + 'win_webrtc_perf_tests_tryserver', + ], + }, +} diff --git a/infra/specs/trybot_analyze_config.json b/infra/specs/trybot_analyze_config.json new file mode 100644 index 0000000000..4489a7d5dd --- /dev/null +++ b/infra/specs/trybot_analyze_config.json @@ -0,0 +1,8 @@ +{ + "base": { + "exclusions": [ + "DEPS", + "infra/specs/.*" + ] + } +} \ No newline at end of file diff --git a/infra/specs/tryserver.webrtc.json b/infra/specs/tryserver.webrtc.json new file mode 100644 index 0000000000..0c4b326801 --- /dev/null +++ b/infra/specs/tryserver.webrtc.json @@ -0,0 +1,14592 @@ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "android_arm64_dbg": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "android_arm64_rel": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "android_arm_dbg": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "android_arm_more_configs": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + }, + "android_arm_rel": { + "gtest_tests": [ + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "AppRTCMobile_test_apk", + "test_id_prefix": "ninja://examples:AppRTCMobile_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "android_instrumentation_test_apk", + "test_id_prefix": "ninja://sdk/android:android_instrumentation_test_apk/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "audio_decoder_unittests", + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_audio_unittests", + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "common_video_unittests", + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "dcsctp_unittests", + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 2 + }, + "test": "modules_tests", + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "modules_unittests", + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "peerconnection_unittests", + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_media_unittests", + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_pc_unittests", + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "rtc_stats_unittests", + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 6 + }, + "test": "rtc_unittests", + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "slow_peer_connection_unittests", + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "system_wrappers_unittests", + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "test_support_unittests", + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "tools_unittests", + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ], + "shards": 4 + }, + "test": "video_engine_tests", + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "voip_unittests", + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_nonparallel_tests", + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs" + ], + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/luci/logdog/butler/${platform}", + "location": "bin", + "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" + } + ], + "dimension_sets": [ + { + "android_devices": "1", + "device_os": "MMB29Q", + "device_type": "bullhead", + "os": "Android" + } + ] + }, + "test": "webrtc_perf_tests", + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ], + "junit_tests": [ + { + "name": "android_examples_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_examples_junit_tests", + "test_id_prefix": "ninja://examples:android_examples_junit_tests/" + }, + { + "name": "android_sdk_junit_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": {}, + "test": "android_sdk_junit_tests", + "test_id_prefix": "ninja://sdk/android:android_sdk_junit_tests/" + } + ] + }, + "android_compile_arm64_dbg": {}, + "android_compile_arm64_rel": {}, + "android_compile_arm_dbg": {}, + "android_compile_arm_rel": {}, + "android_compile_x64_dbg": {}, + "android_compile_x64_rel": {}, + "android_compile_x86_dbg": {}, + "android_compile_x86_rel": {}, + "ios_compile_arm64_dbg": {}, + "ios_compile_arm64_rel": {}, + "ios_sim_x64_dbg_ios12": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "12.4", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_12_4", + "path": "Runtime-ios-12.4" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "ios_sim_x64_dbg_ios13": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "13.6", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_13_6", + "path": "Runtime-ios-13.6" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "ios_sim_x64_dbg_ios14": { + "isolated_scripts": [ + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "apprtcmobile_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "apprtcmobile_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://examples:apprtcmobile_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_framework_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_framework_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_framework_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}", + "--xcode-parallelization" + ], + "isolate_name": "sdk_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "sdk_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://sdk:sdk_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "args": [ + "--platform", + "iPhone X", + "--version", + "14.0", + "--xcode-build-version", + "12a7209", + "--out-dir", + "${ISOLATED_OUTDIR}" + ], + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "cipd_packages": [ + { + "cipd_package": "infra/tools/mac_toolchain/${platform}", + "location": ".", + "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1" + } + ], + "dimension_sets": [ + { + "os": "Mac-11" + } + ], + "named_caches": [ + { + "name": "runtime_ios_14_0", + "path": "Runtime-ios-14.0" + }, + { + "name": "xcode_ios_12a7209", + "path": "Xcode.app" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_asan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_compile_arm64_dbg": {}, + "linux_compile_arm64_rel": {}, + "linux_compile_arm_dbg": {}, + "linux_compile_arm_rel": {}, + "linux_compile_dbg": {}, + "linux_compile_rel": {}, + "linux_dbg": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_libfuzzer_rel": {}, + "linux_memcheck": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_more_configs": { + "isolated_scripts": [ + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + }, + "linux_msan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_rel": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "linux_tsan2": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_ubsan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_ubsan_vptr": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_x86_dbg": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "linux_x86_rel": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Ubuntu-18.04" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "mac_asan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "mac_compile_dbg": {}, + "mac_compile_rel": {}, + "mac_dbg": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cores": "12", + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "mac_dbg_m1": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "mac_rel": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac-11" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Mac", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "mac_rel_m1": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "arm64-64-Apple_M1", + "os": "Mac-11", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_asan": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_compile_x64_clang_dbg": {}, + "win_compile_x64_clang_rel": {}, + "win_compile_x86_clang_dbg": {}, + "win_compile_x86_clang_rel": {}, + "win_x64_clang_dbg": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_x64_clang_dbg_win10": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-10-15063" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_x64_clang_rel": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_x86_clang_dbg": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + } + ] + }, + "win_x86_clang_rel": { + "isolated_scripts": [ + { + "isolate_name": "audio_decoder_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "audio_decoder_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://modules/audio_coding:audio_decoder_unittests/" + }, + { + "isolate_name": "common_audio_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_audio_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_audio:common_audio_unittests/" + }, + { + "isolate_name": "common_video_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "common_video_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://common_video:common_video_unittests/" + }, + { + "isolate_name": "dcsctp_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "dcsctp_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://net/dcsctp:dcsctp_unittests/" + }, + { + "args": [ + "--quick" + ], + "isolate_name": "low_bandwidth_audio_test", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "low_bandwidth_audio_test", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://audio:low_bandwidth_audio_test/" + }, + { + "isolate_name": "modules_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 2 + }, + "test_id_prefix": "ninja://modules:modules_tests/" + }, + { + "isolate_name": "modules_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "modules_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://modules:modules_unittests/" + }, + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + }, + { + "isolate_name": "rtc_media_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_media_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://media:rtc_media_unittests/" + }, + { + "isolate_name": "rtc_pc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_pc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:rtc_pc_unittests/" + }, + { + "isolate_name": "rtc_stats_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_stats_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://stats:rtc_stats_unittests/" + }, + { + "isolate_name": "rtc_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "rtc_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 6 + }, + "test_id_prefix": "ninja://:rtc_unittests/" + }, + { + "isolate_name": "slow_peer_connection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "slow_peer_connection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://pc:slow_peer_connection_unittests/" + }, + { + "isolate_name": "system_wrappers_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "system_wrappers_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://system_wrappers:system_wrappers_unittests/" + }, + { + "isolate_name": "test_support_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "test_support_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://test:test_support_unittests/" + }, + { + "isolate_name": "tools_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "tools_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://rtc_tools:tools_unittests/" + }, + { + "isolate_name": "video_capture_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_capture_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://modules/video_capture:video_capture_tests/" + }, + { + "isolate_name": "video_engine_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "video_engine_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://:video_engine_tests/" + }, + { + "isolate_name": "voip_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "voip_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:voip_unittests/" + }, + { + "isolate_name": "webrtc_nonparallel_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_nonparallel_tests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_nonparallel_tests/" + }, + { + "args": [ + "--force_fieldtrials=WebRTC-QuickPerfTest/Enabled/", + "--nologs", + "--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json" + ], + "isolate_name": "webrtc_perf_tests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "webrtc_perf_tests", + "resultdb": { + "result_file": "${ISOLATED_OUTDIR}/gtest_output.json", + "result_format": "gtest_json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows", + "pool": "WebRTC-baremetal-try" + } + ] + }, + "test_id_prefix": "ninja://:webrtc_perf_tests/" + } + ] + }, + "win_x86_more_configs": { + "isolated_scripts": [ + { + "isolate_name": "peerconnection_unittests", + "merge": { + "args": [], + "script": "//testing/merge_scripts/standard_isolated_script_merge.py" + }, + "name": "peerconnection_unittests", + "resultdb": { + "result_format": "json" + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "os": "Windows-7-SP1" + } + ], + "shards": 4 + }, + "test_id_prefix": "ninja://pc:peerconnection_unittests/" + } + ] + } +} diff --git a/infra/specs/variants.pyl b/infra/specs/variants.pyl new file mode 100644 index 0000000000..c31ab89d2e --- /dev/null +++ b/infra/specs/variants.pyl @@ -0,0 +1,9 @@ +# Copyright (c) 2022 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. + +{} \ No newline at end of file diff --git a/infra/specs/waterfalls.pyl b/infra/specs/waterfalls.pyl new file mode 100644 index 0000000000..5b51b87652 --- /dev/null +++ b/infra/specs/waterfalls.pyl @@ -0,0 +1,628 @@ +# Copyright (c) 2022 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. + +[ + { + 'name': 'client.webrtc', + 'mixins': [], + 'machines': { + 'Android32 (M Nexus5X)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests', + 'junit_tests': 'android_junit_tests', + }, + }, + 'Android32 (M Nexus5X)(dbg)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests', + 'junit_tests': 'android_junit_tests', + }, + }, + 'Android32 (more configs)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'more_configs_tests', + }, + }, + 'Android32 Builder arm': {}, + 'Android32 Builder x86': {}, + 'Android32 Builder x86 (dbg)': {}, + 'Android64 (M Nexus5X)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests', + 'junit_tests': 'android_junit_tests', + }, + }, + 'Android64 (M Nexus5X)(dbg)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests', + 'junit_tests': 'android_junit_tests', + }, + }, + 'Android64 Builder arm64': {}, + 'Android64 Builder x64 (dbg)': {}, + 'Linux (more configs)': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'more_configs_tests', + }, + }, + 'Linux Asan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux MSan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux Tsan v2': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux UBSan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux UBSan vptr': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux32 Debug': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux32 Debug (ARM)': {}, + 'Linux32 Release': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux32 Release (ARM)': {}, + 'Linux64 Builder': {}, + 'Linux64 Debug': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Linux64 Debug (ARM)': {}, + 'Linux64 Release': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'linux_tests', + }, + }, + 'Linux64 Release (ARM)': {}, + 'Linux64 Release (Libfuzzer)': {}, + 'Mac Asan': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'cores-12', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Mac64 Builder': {}, + 'Mac64 Debug': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'cores-12', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Mac64 Release': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'mac_tests', + }, + }, + 'MacARM64 M1 Release': { + 'os_type': + 'mac', + 'mixins': + ['mac11', 'mac-m1-cpu', 'baremetal-pool', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'MacArm64 Builder': {}, + 'Win (more configs)': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'more_configs_tests', + }, + }, + 'Win32 Builder (Clang)': {}, + 'Win32 Debug (Clang)': {}, + 'Win32 Release (Clang)': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'win_tests', + }, + }, + 'Win64 ASan': { + 'os_type': 'win', + 'mixins': ['win10-1703', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'Win64 Debug (Clang)': {}, + 'Win64 Release (Clang)': {}, + 'iOS64 Debug': {}, + 'iOS64 Release': {}, + 'iOS64 Sim Debug (iOS 12)': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-12.4', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + 'iOS64 Sim Debug (iOS 13)': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-13.6', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + 'iOS64 Sim Debug (iOS 14.0)': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-14.0', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + }, + }, + { + 'name': 'client.webrtc.perf', + 'mixins': [], + 'machines': { + 'Perf Android32 (M AOSP Nexus6)': { + 'mixins': [ + 'shamu', 'marshmallow_generic', 'android-devices', 'perf-pool', + 'perf-output', 'timeout-3h', 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_perf_tests', + }, + }, + 'Perf Android32 (M Nexus5)': { + 'mixins': [ + 'hammerhead', 'marshmallow_generic', 'android-devices', 'perf-pool', + 'perf-output', 'timeout-3h', 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_perf_tests', + }, + }, + 'Perf Android64 (M Nexus5X)': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'perf-pool', + 'perf-output', 'timeout-3h', 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_perf_tests', + }, + }, + 'Perf Android64 (O Pixel2)': { + 'mixins': [ + 'walleye', 'android-devices', 'perf-pool', 'timeout-3h', + 'perf-output', 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_perf_tests', + }, + }, + 'Perf Linux Bionic': { + 'os_type': + 'linux', + 'mixins': [ + 'linux-bionic', 'perf-pool', 'timeout-3h', + 'resultdb-gtest-json-format' + ], + 'test_suites': { + 'isolated_scripts': 'desktop_perf_tests', + }, + }, + 'Perf Mac 11': { + 'os_type': + 'mac', + 'mixins': [ + 'mac11', 'x86-64', 'perf-pool', 'timeout-3h', + 'resultdb-gtest-json-format' + ], + 'test_suites': { + 'isolated_scripts': 'desktop_perf_tests', + }, + }, + 'Perf Mac M1 Arm64 12': { + 'os_type': + 'mac', + 'mixins': [ + 'mac_12_beta_arm64', 'mac-m1-cpu', 'perf-pool', 'timeout-3h', + 'resultdb-gtest-json-format' + ], + 'test_suites': { + 'isolated_scripts': 'desktop_perf_tests', + }, + }, + 'Perf Win7': { + 'os_type': + 'win', + 'mixins': + ['win', 'perf-pool', 'timeout-3h', 'resultdb-gtest-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_perf_tests', + }, + }, + }, + }, + { + 'name': 'internal.client.webrtc', + 'mixins': [], + 'machines': { + 'iOS64 Debug': { + 'mixins': [ + 'ios-device-15.3', 'webrtc-xctest', 'chrome-tester-service-account', + 'xcode_13_main', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_device_tests', + }, + }, + 'iOS64 Perf': { + 'mixins': [ + 'ios-device-perf', 'timeout-3h', 'chrome-tester-service-account', + 'xcode_12d4e', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_perf_tests', + }, + }, + 'iOS64 Release': { + 'mixins': [ + 'ios-device-15.3', 'webrtc-xctest', 'chrome-tester-service-account', + 'xcode_13_main', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_device_tests', + }, + }, + }, + }, + { + 'name': 'tryserver.webrtc', + 'mixins': [], + 'machines': { + 'android_arm64_dbg': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests_tryserver', + 'junit_tests': 'android_junit_tests', + }, + }, + 'android_arm64_rel': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests_tryserver', + 'junit_tests': 'android_junit_tests', + }, + }, + 'android_arm_dbg': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests_tryserver', + 'junit_tests': 'android_junit_tests', + }, + }, + 'android_arm_more_configs': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'more_configs_tests', + }, + }, + 'android_arm_rel': { + 'mixins': [ + 'bullhead', 'marshmallow', 'android-devices', 'logdog-butler', + 'has_native_resultdb_integration' + ], + 'test_suites': { + 'gtest_tests': 'android_tests_tryserver', + 'junit_tests': 'android_junit_tests', + }, + }, + 'android_compile_arm64_dbg': {}, + 'android_compile_arm64_rel': {}, + 'android_compile_arm_dbg': {}, + 'android_compile_arm_rel': {}, + 'android_compile_x64_dbg': {}, + 'android_compile_x64_rel': {}, + 'android_compile_x86_dbg': {}, + 'android_compile_x86_rel': {}, + 'ios_compile_arm64_dbg': {}, + 'ios_compile_arm64_rel': {}, + 'ios_sim_x64_dbg_ios12': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-12.4', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + 'ios_sim_x64_dbg_ios13': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-13.6', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + 'ios_sim_x64_dbg_ios14': { + 'mixins': [ + 'mac11', 'chromium-tester-service-account', 'ios-simulator-14.0', + 'xcode_12a7209', 'mac_toolchain', 'has_native_resultdb_integration', + 'out_dir_arg' + ], + 'test_suites': { + 'isolated_scripts': 'ios_simulator_tests', + }, + }, + 'linux_asan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_compile_arm64_dbg': {}, + 'linux_compile_arm64_rel': {}, + 'linux_compile_arm_dbg': {}, + 'linux_compile_arm_rel': {}, + 'linux_compile_dbg': {}, + 'linux_compile_rel': {}, + 'linux_dbg': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_libfuzzer_rel': {}, + 'linux_memcheck': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_more_configs': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'more_configs_tests', + }, + }, + 'linux_msan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_rel': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'linux_tests_tryserver', + }, + }, + 'linux_tsan2': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_ubsan': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_ubsan_vptr': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_x86_dbg': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'linux_x86_rel': { + 'os_type': 'linux', + 'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'mac_asan': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'resultdb-json-format', 'cores-12'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'mac_compile_dbg': {}, + 'mac_compile_rel': {}, + 'mac_dbg': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'resultdb-json-format', 'cores-12'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'mac_dbg_m1': { + 'os_type': + 'mac', + 'mixins': + ['mac11', 'mac-m1-cpu', 'baremetal-try-pool', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'mac_rel': { + 'os_type': 'mac', + 'mixins': ['mac11', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'mac_tests_tryserver', + }, + }, + 'mac_rel_m1': { + 'os_type': + 'mac', + 'mixins': + ['mac11', 'mac-m1-cpu', 'baremetal-try-pool', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_asan': { + 'os_type': 'win', + 'mixins': ['win10-1703', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_compile_x64_clang_dbg': {}, + 'win_compile_x64_clang_rel': {}, + 'win_compile_x86_clang_dbg': {}, + 'win_compile_x86_clang_rel': {}, + 'win_x64_clang_dbg': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_x64_clang_dbg_win10': { + 'os_type': 'win', + 'mixins': ['win10-1703', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_x64_clang_rel': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_x86_clang_dbg': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'desktop_tests', + }, + }, + 'win_x86_clang_rel': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'win_tests_tryserver', + }, + }, + 'win_x86_more_configs': { + 'os_type': 'win', + 'mixins': ['win7', 'x86-64', 'resultdb-json-format'], + 'test_suites': { + 'isolated_scripts': 'more_configs_tests', + }, + }, + }, + }, +] diff --git a/logging/BUILD.gn b/logging/BUILD.gn index 1b7993104e..d8b4b8493b 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -54,7 +54,6 @@ rtc_library("rtc_event_field") { "../rtc_base:bitstream_reader", "../rtc_base:checks", "../rtc_base:logging", - "../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings", @@ -226,8 +225,8 @@ rtc_library("rtc_event_rtp_rtcp") { "../api/rtc_event_log", "../api/units:timestamp", "../modules/rtp_rtcp:rtp_rtcp_format", + "../rtc_base:buffer", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", @@ -269,11 +268,11 @@ rtc_library("rtc_event_number_encodings") { defines = [] deps = [ + "../rtc_base:bit_buffer", "../rtc_base:bitstream_reader", "../rtc_base:checks", "../rtc_base:ignore_wundef", "../rtc_base:macromagic", - "../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", @@ -299,10 +298,13 @@ rtc_library("rtc_event_log_impl_encoder") { "../api:rtp_headers", "../api:rtp_parameters", "../api/transport:network_control", + "../rtc_base:bit_buffer", "../rtc_base:bitstream_reader", + "../rtc_base:buffer", "../rtc_base:checks", "../rtc_base:ignore_wundef", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:safe_conversions", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", @@ -365,9 +367,13 @@ if (rtc_enable_protobuf) { "../api/rtc_event_log", "../api/task_queue", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", + "../rtc_base:safe_conversions", "../rtc_base:safe_minmax", + "../rtc_base:timeutils", "../rtc_base/system:no_unique_address", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -386,6 +392,7 @@ rtc_library("fake_rtc_event_log") { deps = [ "../api/rtc_event_log", "../rtc_base", + "../rtc_base:macromagic", "../rtc_base/synchronization:mutex", ] } @@ -442,10 +449,12 @@ if (rtc_enable_protobuf) { "../modules/audio_coding:audio_network_adaptor", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", "../rtc_base:ignore_wundef", + "../rtc_base:logging", "../rtc_base:protobuf_utils", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_numerics", + "../rtc_base:safe_conversions", "../rtc_base/system:file_wrapper", ] absl_deps = [ @@ -498,9 +507,12 @@ if (rtc_enable_protobuf) { "../call:call_interfaces", "../modules/audio_coding:audio_network_adaptor", "../modules/rtp_rtcp:rtp_rtcp_format", + "../rtc_base:buffer", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", + "../rtc_base:random", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:timeutils", "../system_wrappers", "../test:fileutils", "../test:test_support", @@ -526,7 +538,6 @@ if (rtc_enable_protobuf) { "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", "../rtc_base:protobuf_utils", - "../rtc_base:rtc_base_approved", "../test:rtp_test_utils", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", @@ -559,7 +570,6 @@ rtc_library("ice_log") { "../api:libjingle_peerconnection_api", # For api/dtls_transport_interface.h "../api/rtc_event_log", "../api/units:timestamp", - "../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/memory", diff --git a/logging/rtc_event_log/events/logged_rtp_rtcp.h b/logging/rtc_event_log/events/logged_rtp_rtcp.h index 179d70dd63..053a16371d 100644 --- a/logging/rtc_event_log/events/logged_rtp_rtcp.h +++ b/logging/rtc_event_log/events/logged_rtp_rtcp.h @@ -41,6 +41,7 @@ struct LoggedRtpPacket { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp; // TODO(terelius): This allocates space for 15 CSRCs even if none are used. @@ -57,6 +58,7 @@ struct LoggedRtpPacketIncoming { : rtp(timestamp, header, header_length, total_length) {} int64_t log_time_us() const { return rtp.timestamp.us(); } int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } LoggedRtpPacket rtp; }; @@ -69,6 +71,7 @@ struct LoggedRtpPacketOutgoing { : rtp(timestamp, header, header_length, total_length) {} int64_t log_time_us() const { return rtp.timestamp.us(); } int64_t log_time_ms() const { return rtp.timestamp.ms(); } + Timestamp log_time() const { return rtp.timestamp; } LoggedRtpPacket rtp; }; @@ -87,6 +90,7 @@ struct LoggedRtcpPacket { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp; std::vector raw_data; @@ -101,6 +105,7 @@ struct LoggedRtcpPacketIncoming { int64_t log_time_us() const { return rtcp.timestamp.us(); } int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } LoggedRtcpPacket rtcp; }; @@ -114,6 +119,7 @@ struct LoggedRtcpPacketOutgoing { int64_t log_time_us() const { return rtcp.timestamp.us(); } int64_t log_time_ms() const { return rtcp.timestamp.ms(); } + Timestamp log_time() const { return rtcp.timestamp; } LoggedRtcpPacket rtcp; }; @@ -126,6 +132,7 @@ struct LoggedRtcpPacketReceiverReport { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::ReceiverReport rr; @@ -139,6 +146,7 @@ struct LoggedRtcpPacketSenderReport { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::SenderReport sr; @@ -149,6 +157,7 @@ struct LoggedRtcpPacketExtendedReports { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::ExtendedReports xr; @@ -161,6 +170,7 @@ struct LoggedRtcpPacketRemb { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::Remb remb; @@ -173,6 +183,7 @@ struct LoggedRtcpPacketNack { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::Nack nack; @@ -183,6 +194,7 @@ struct LoggedRtcpPacketFir { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::Fir fir; @@ -193,6 +205,7 @@ struct LoggedRtcpPacketPli { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::Pli pli; @@ -209,6 +222,7 @@ struct LoggedRtcpPacketTransportFeedback { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::TransportFeedback transport_feedback; @@ -223,6 +237,7 @@ struct LoggedRtcpPacketLossNotification { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::LossNotification loss_notification; @@ -233,6 +248,7 @@ struct LoggedRtcpPacketBye { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtcp::Bye bye; diff --git a/logging/rtc_event_log/events/rtc_event_alr_state.h b/logging/rtc_event_log/events/rtc_event_alr_state.h index 44e68a680e..9f595ecd90 100644 --- a/logging/rtc_event_log/events/rtc_event_alr_state.h +++ b/logging/rtc_event_log/events/rtc_event_alr_state.h @@ -32,6 +32,7 @@ struct LoggedAlrStateEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); bool in_alr; diff --git a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h index b9e919c2e8..d4cae3abfa 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h +++ b/logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h @@ -31,6 +31,7 @@ struct LoggedAudioNetworkAdaptationEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); AudioEncoderRuntimeConfig config; diff --git a/logging/rtc_event_log/events/rtc_event_audio_playout.h b/logging/rtc_event_log/events/rtc_event_audio_playout.h index 3788324d15..196c3ca247 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_playout.h +++ b/logging/rtc_event_log/events/rtc_event_audio_playout.h @@ -32,6 +32,7 @@ struct LoggedAudioPlayoutEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); uint32_t ssrc; diff --git a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h index ac8813c937..9863e235af 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h @@ -30,6 +30,7 @@ struct LoggedAudioRecvConfig { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtclog::StreamConfig config; diff --git a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h index 8b699e71cf..550723bcf0 100644 --- a/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h @@ -29,6 +29,7 @@ struct LoggedAudioSendConfig { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtclog::StreamConfig config; diff --git a/logging/rtc_event_log/events/rtc_event_begin_log.h b/logging/rtc_event_log/events/rtc_event_begin_log.h index 8dc47b5a02..f3b74c117e 100644 --- a/logging/rtc_event_log/events/rtc_event_begin_log.h +++ b/logging/rtc_event_log/events/rtc_event_begin_log.h @@ -35,6 +35,7 @@ struct LoggedStartEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp utc_time() const { return utc_start_time; } diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h index 4f145de412..796f119388 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h @@ -76,6 +76,7 @@ struct LoggedBweDelayBasedUpdate { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int32_t bitrate_bps; diff --git a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h index 1caf818a0c..fd41b316e0 100644 --- a/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h +++ b/logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h @@ -37,6 +37,7 @@ struct LoggedBweLossBasedUpdate { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int32_t bitrate_bps; diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h index c2496dd532..b9af213256 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h @@ -26,6 +26,7 @@ namespace webrtc { struct LoggedDtlsTransportState { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); DtlsTransportState dtls_transport_state; diff --git a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h index 20221df9da..c820f184d7 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_writable_state.h @@ -28,6 +28,7 @@ struct LoggedDtlsWritableState { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); bool writable; diff --git a/logging/rtc_event_log/events/rtc_event_end_log.h b/logging/rtc_event_log/events/rtc_event_end_log.h index ed7770f339..79648bdb8d 100644 --- a/logging/rtc_event_log/events/rtc_event_end_log.h +++ b/logging/rtc_event_log/events/rtc_event_end_log.h @@ -32,6 +32,7 @@ struct LoggedStopEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::PlusInfinity(); }; diff --git a/logging/rtc_event_log/events/rtc_event_frame_decoded.h b/logging/rtc_event_log/events/rtc_event_frame_decoded.h index db5162cd0d..91190faea9 100644 --- a/logging/rtc_event_log/events/rtc_event_frame_decoded.h +++ b/logging/rtc_event_log/events/rtc_event_frame_decoded.h @@ -29,6 +29,7 @@ namespace webrtc { struct LoggedFrameDecoded { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int64_t render_time_ms; diff --git a/logging/rtc_event_log/events/rtc_event_generic_ack_received.h b/logging/rtc_event_log/events/rtc_event_generic_ack_received.h index d1ad0f1fef..57fd7cd9a6 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_ack_received.h +++ b/logging/rtc_event_log/events/rtc_event_generic_ack_received.h @@ -36,6 +36,7 @@ struct LoggedGenericAckReceived { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int64_t packet_number; diff --git a/logging/rtc_event_log/events/rtc_event_generic_packet_received.h b/logging/rtc_event_log/events/rtc_event_generic_packet_received.h index fddee67740..a6006ca4d4 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_packet_received.h +++ b/logging/rtc_event_log/events/rtc_event_generic_packet_received.h @@ -33,6 +33,7 @@ struct LoggedGenericPacketReceived { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int64_t packet_number; diff --git a/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h b/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h index 0f839ca949..903950a398 100644 --- a/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h +++ b/logging/rtc_event_log/events/rtc_event_generic_packet_sent.h @@ -37,6 +37,7 @@ struct LoggedGenericPacketSent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } size_t packet_length() const { return payload_length + padding_length + overhead_length; diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h index 85cf79735a..bdacf15a59 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h @@ -45,6 +45,7 @@ struct LoggedIceCandidatePairEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); IceCandidatePairEventType type; diff --git a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h index 0dfbfc838a..e72d999cff 100644 --- a/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h +++ b/logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h @@ -72,6 +72,7 @@ enum class IceCandidateNetworkType { struct LoggedIceCandidatePairConfig { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); IceCandidatePairConfigType type; diff --git a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h index b91d89c239..ae6810c39d 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h +++ b/logging/rtc_event_log/events/rtc_event_probe_cluster_created.h @@ -39,6 +39,7 @@ struct LoggedBweProbeClusterCreatedEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int32_t id; diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h index ba4db75c66..1aa6e75cb7 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_failure.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_failure.h @@ -40,6 +40,7 @@ struct LoggedBweProbeFailureEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int32_t id; diff --git a/logging/rtc_event_log/events/rtc_event_probe_result_success.h b/logging/rtc_event_log/events/rtc_event_probe_result_success.h index 172e9aa2eb..49d1abec5a 100644 --- a/logging/rtc_event_log/events/rtc_event_probe_result_success.h +++ b/logging/rtc_event_log/events/rtc_event_probe_result_success.h @@ -33,6 +33,7 @@ struct LoggedBweProbeSuccessEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); int32_t id; diff --git a/logging/rtc_event_log/events/rtc_event_remote_estimate.h b/logging/rtc_event_log/events/rtc_event_remote_estimate.h index 17de63c475..4a39ecc597 100644 --- a/logging/rtc_event_log/events/rtc_event_remote_estimate.h +++ b/logging/rtc_event_log/events/rtc_event_remote_estimate.h @@ -28,6 +28,7 @@ struct LoggedRemoteEstimateEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); absl::optional link_capacity_lower; diff --git a/logging/rtc_event_log/events/rtc_event_route_change.h b/logging/rtc_event_log/events/rtc_event_route_change.h index 542d15e3b3..bc1461d7bb 100644 --- a/logging/rtc_event_log/events/rtc_event_route_change.h +++ b/logging/rtc_event_log/events/rtc_event_route_change.h @@ -29,6 +29,7 @@ struct LoggedRouteChangeEvent { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); bool connected; diff --git a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h index f3380d1bb6..0be56c2065 100644 --- a/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h @@ -30,6 +30,7 @@ struct LoggedVideoRecvConfig { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtclog::StreamConfig config; diff --git a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h index 15c28999dc..f1717b19ea 100644 --- a/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h +++ b/logging/rtc_event_log/events/rtc_event_video_send_stream_config.h @@ -30,6 +30,7 @@ struct LoggedVideoSendConfig { int64_t log_time_us() const { return timestamp.us(); } int64_t log_time_ms() const { return timestamp.ms(); } + Timestamp log_time() const { return timestamp; } Timestamp timestamp = Timestamp::MinusInfinity(); rtclog::StreamConfig config; diff --git a/logging/rtc_event_log/rtc_event_log_parser.cc b/logging/rtc_event_log/rtc_event_log_parser.cc index f0d165bdb2..4033a593a1 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.cc +++ b/logging/rtc_event_log/rtc_event_log_parser.cc @@ -38,6 +38,7 @@ #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/numerics/sequence_number_util.h" @@ -1110,8 +1111,8 @@ void ParsedRtcEventLog::Clear() { last_incoming_rtcp_packet_.clear(); - first_timestamp_ = std::numeric_limits::max(); - last_timestamp_ = std::numeric_limits::min(); + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); first_log_segment_ = LogSegment(0, std::numeric_limits::max()); incoming_rtp_extensions_maps_.clear(); @@ -1232,8 +1233,8 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( // stream configurations and starting/stopping the log. // TODO(terelius): Figure out if we actually need to find the first and last // timestamp in the parser. It seems like this could be done by the caller. - first_timestamp_ = std::numeric_limits::max(); - last_timestamp_ = std::numeric_limits::min(); + first_timestamp_ = Timestamp::PlusInfinity(); + last_timestamp_ = Timestamp::MinusInfinity(); StoreFirstAndLastTimestamp(alr_state_events()); StoreFirstAndLastTimestamp(route_change_events()); for (const auto& audio_stream : audio_playout_events()) { @@ -1272,7 +1273,8 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( // event, we could use the timestamp of the the last previous regular event. auto start_iter = start_log_events().begin(); auto stop_iter = stop_log_events().begin(); - int64_t start_us = first_timestamp(); + int64_t start_us = + first_timestamp().us_or(std::numeric_limits::max()); int64_t next_start_us = std::numeric_limits::max(); int64_t stop_us = std::numeric_limits::max(); if (start_iter != start_log_events().end()) { @@ -1286,15 +1288,14 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::ParseStream( } stop_us = std::min(stop_us, next_start_us); if (stop_us == std::numeric_limits::max() && - last_timestamp() != std::numeric_limits::min()) { - stop_us = last_timestamp(); + !last_timestamp().IsMinusInfinity()) { + stop_us = last_timestamp().us(); } RTC_PARSE_CHECK_OR_RETURN_LE(start_us, stop_us); first_log_segment_ = LogSegment(start_us, stop_us); - if (first_timestamp_ == std::numeric_limits::max() && - last_timestamp_ == std::numeric_limits::min()) { - first_timestamp_ = last_timestamp_ = 0; + if (first_timestamp_ > last_timestamp_) { + first_timestamp_ = last_timestamp_ = Timestamp::Zero(); } return status; @@ -1563,8 +1564,8 @@ template void ParsedRtcEventLog::StoreFirstAndLastTimestamp(const std::vector& v) { if (v.empty()) return; - first_timestamp_ = std::min(first_timestamp_, v.front().log_time_us()); - last_timestamp_ = std::max(last_timestamp_, v.back().log_time_us()); + first_timestamp_ = std::min(first_timestamp_, v.front().log_time()); + last_timestamp_ = std::max(last_timestamp_, v.back().log_time()); } ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedLegacyEvent( @@ -1637,7 +1638,8 @@ ParsedRtcEventLog::ParseStatus ParsedRtcEventLog::StoreParsedLegacyEvent( // Use RtpPacketReceived instead of more generic RtpPacket because former // has a buildin convertion to RTPHeader. RtpPacketReceived rtp_header; - RTC_PARSE_CHECK_OR_RETURN(rtp_header.Parse(rtp_packet.header())); + RTC_PARSE_CHECK_OR_RETURN( + rtp_header.Parse(rtc::CopyOnWriteBuffer(rtp_packet.header()))); if (const RtpHeaderExtensionMap* extension_map = GetRtpHeaderExtensionMap( rtp_packet.incoming(), rtp_header.Ssrc())) { diff --git a/logging/rtc_event_log/rtc_event_log_parser.h b/logging/rtc_event_log/rtc_event_log_parser.h index 4a9ff3495f..9ef4e347de 100644 --- a/logging/rtc_event_log/rtc_event_log_parser.h +++ b/logging/rtc_event_log/rtc_event_log_parser.h @@ -643,8 +643,8 @@ class ParsedRtcEventLog { return decoded_frames_; } - int64_t first_timestamp() const { return first_timestamp_; } - int64_t last_timestamp() const { return last_timestamp_; } + Timestamp first_timestamp() const { return first_timestamp_; } + Timestamp last_timestamp() const { return last_timestamp_; } const LogSegment& first_log_segment() const { return first_log_segment_; } @@ -889,8 +889,8 @@ class ParsedRtcEventLog { std::vector last_incoming_rtcp_packet_; - int64_t first_timestamp_; - int64_t last_timestamp_; + Timestamp first_timestamp_ = Timestamp::PlusInfinity(); + Timestamp last_timestamp_ = Timestamp::MinusInfinity(); LogSegment first_log_segment_ = LogSegment(0, std::numeric_limits::max()); diff --git a/logging/rtc_event_log/rtc_event_log_unittest.cc b/logging/rtc_event_log/rtc_event_log_unittest.cc index 8f32eecd8b..314bfd90e9 100644 --- a/logging/rtc_event_log/rtc_event_log_unittest.cc +++ b/logging/rtc_event_log/rtc_event_log_unittest.cc @@ -785,8 +785,8 @@ void RtcEventLogSession::ReadAndVerifyLog() { parsed_generic_acks_received[i]); } - EXPECT_EQ(first_timestamp_ms_, parsed_log.first_timestamp() / 1000); - EXPECT_EQ(last_timestamp_ms_, parsed_log.last_timestamp() / 1000); + EXPECT_EQ(first_timestamp_ms_, parsed_log.first_timestamp().ms()); + EXPECT_EQ(last_timestamp_ms_, parsed_log.last_timestamp().ms()); EXPECT_EQ(parsed_log.first_log_segment().start_time_ms(), std::min(start_time_us_ / 1000, first_timestamp_ms_)); @@ -904,31 +904,39 @@ TEST_P(RtcEventLogCircularBufferTest, KeepsMostRecentEvents) { std::make_unique(); fake_clock->SetTime(Timestamp::Seconds(kStartTimeSeconds)); - auto task_queue_factory = CreateDefaultTaskQueueFactory(); - RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); - // When `log` goes out of scope, the contents are flushed - // to the output. - std::unique_ptr log = - rtc_event_log_factory.CreateRtcEventLog(encoding_type_); - - for (size_t i = 0; i < kNumEvents; i++) { - // The purpose of the test is to verify that the log can handle - // more events than what fits in the internal circular buffer. The exact - // type of events does not matter so we chose ProbeSuccess events for - // simplicity. - // We base the various values on the index. We use this for some basic - // consistency checks when we read back. - log->Log(std::make_unique( - i, kStartBitrate + i * 1000)); + // Create a scope for the TQ and event log factories. + // This way, we make sure that task queue instances that may rely on a clock + // have been torn down before we run the verification steps at the end of + // the test. + int64_t start_time_us, utc_start_time_us, stop_time_us; + + { + auto task_queue_factory = CreateDefaultTaskQueueFactory(); + RtcEventLogFactory rtc_event_log_factory(task_queue_factory.get()); + // When `log` goes out of scope, the contents are flushed + // to the output. + std::unique_ptr log = + rtc_event_log_factory.CreateRtcEventLog(encoding_type_); + + for (size_t i = 0; i < kNumEvents; i++) { + // The purpose of the test is to verify that the log can handle + // more events than what fits in the internal circular buffer. The exact + // type of events does not matter so we chose ProbeSuccess events for + // simplicity. + // We base the various values on the index. We use this for some basic + // consistency checks when we read back. + log->Log(std::make_unique( + i, kStartBitrate + i * 1000)); + fake_clock->AdvanceTime(TimeDelta::Millis(10)); + } + start_time_us = rtc::TimeMicros(); + utc_start_time_us = rtc::TimeUTCMicros(); + log->StartLogging(log_output_factory_->Create(temp_filename), + RtcEventLog::kImmediateOutput); fake_clock->AdvanceTime(TimeDelta::Millis(10)); + stop_time_us = rtc::TimeMicros(); + log->StopLogging(); } - int64_t start_time_us = rtc::TimeMicros(); - int64_t utc_start_time_us = rtc::TimeUTCMicros(); - log->StartLogging(log_output_factory_->Create(temp_filename), - RtcEventLog::kImmediateOutput); - fake_clock->AdvanceTime(TimeDelta::Millis(10)); - int64_t stop_time_us = rtc::TimeMicros(); - log->StopLogging(); // Read the generated log from memory. ParsedRtcEventLog parsed_log; diff --git a/media/BUILD.gn b/media/BUILD.gn index 493b883949..5079b7f396 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -51,6 +51,7 @@ rtc_library("rtc_media_base") { ":rtc_media_config", "../api:array_view", "../api:audio_options_api", + "../api:field_trials_view", "../api:frame_transformer_interface", "../api:media_stream_interface", "../api:rtc_error", @@ -64,7 +65,6 @@ rtc_library("rtc_media_base") { "../api/crypto:options", "../api/transport:datagram_transport_interface", "../api/transport:stun_types", - "../api/transport:webrtc_key_value_config", "../api/transport/rtp:rtp_source", "../api/units:time_delta", "../api/video:video_bitrate_allocation", @@ -79,13 +79,17 @@ rtc_library("rtc_media_base") { "../modules/audio_processing:audio_processing_statistics", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:byte_order", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", - "../rtc_base:rtc_base_approved", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:rtc_task_queue", "../rtc_base:sanitizer", "../rtc_base:socket", "../rtc_base:stringutils", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/system:file_wrapper", "../rtc_base/system:no_unique_address", @@ -154,8 +158,9 @@ rtc_library("rtc_simulcast_encoder_adapter") { "../common_video", "../modules/video_coding:video_codec_interface", "../modules/video_coding:video_coding_utility", + "../rtc_base:atomicops", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", "../rtc_base/experiments:encoder_info_settings", "../rtc_base/experiments:rate_control_settings", "../rtc_base/system:no_unique_address", @@ -209,11 +214,14 @@ rtc_library("rtc_internal_video_codecs") { "../modules/video_coding:webrtc_h264", "../modules/video_coding:webrtc_multiplex", "../modules/video_coding:webrtc_vp8", + "../modules/video_coding:webrtc_vp8_scalability", "../modules/video_coding:webrtc_vp9", + "../modules/video_coding/codecs/av1:av1_svc_config", "../modules/video_coding/codecs/av1:libaom_av1_decoder", "../modules/video_coding/codecs/av1:libaom_av1_encoder_if_supported", + "../modules/video_coding/svc:scalability_mode_util", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", "../rtc_base/system:rtc_export", "../system_wrappers:field_trial", "../test:fake_video_codecs", @@ -249,6 +257,7 @@ rtc_library("rtc_audio_video") { deps = [ ":rtc_media_base", "../api:call_api", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:rtp_parameters", @@ -261,7 +270,6 @@ rtc_library("rtc_audio_video") { "../api/task_queue", "../api/transport:bitrate_settings", "../api/transport:field_trial_based_config", - "../api/transport:webrtc_key_value_config", "../api/transport/rtp:rtp_source", "../api/units:data_rate", "../api/video:video_bitrate_allocation", @@ -286,13 +294,23 @@ rtc_library("rtc_audio_video") { "../modules/video_coding", "../modules/video_coding:video_codec_interface", "../modules/video_coding:video_coding_utility", + "../modules/video_coding/svc:scalability_mode_util", "../rtc_base", "../rtc_base:audio_format_to_string", + "../rtc_base:buffer", + "../rtc_base:byte_order", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", "../rtc_base:ignore_wundef", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:race_checker", "../rtc_base:rtc_task_queue", + "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/experiments:field_trial_parser", "../rtc_base/experiments:min_video_bitrate_experiment", "../rtc_base/experiments:normalize_simulcast_size_experiment", @@ -377,7 +395,7 @@ rtc_source_set("rtc_data_sctp_transport_internal") { "../api/transport:datagram_transport_interface", "../media:rtc_media_base", "../p2p:rtc_p2p", - "../rtc_base:rtc_base_approved", + "../rtc_base:copy_on_write_buffer", "../rtc_base:threading", "../rtc_base/third_party/sigslot", ] @@ -401,9 +419,14 @@ if (rtc_build_dcsctp) { "../net/dcsctp/timer:task_queue_timeout", "../p2p:rtc_p2p", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:logging", + "../rtc_base:random", "../rtc_base:socket", + "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base/containers:flat_map", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/sigslot:sigslot", @@ -416,35 +439,6 @@ if (rtc_build_dcsctp) { } } -if (rtc_build_usrsctp) { - rtc_library("rtc_data_usrsctp_transport") { - defines = [ - # "SCTP_DEBUG" # Uncomment for SCTP debugging. - ] - sources = [ - "sctp/usrsctp_transport.cc", - "sctp/usrsctp_transport.h", - ] - deps = [ - ":rtc_data_sctp_transport_internal", - "../media:rtc_media_base", - "../p2p:rtc_p2p", - "../rtc_base", - "../rtc_base:rtc_base_approved", - "../rtc_base:threading", - "../rtc_base/synchronization:mutex", - "../rtc_base/task_utils:pending_task_safety_flag", - "../rtc_base/task_utils:to_queued_task", - "../rtc_base/third_party/sigslot:sigslot", - "//third_party/usrsctp", - ] - absl_deps = [ - "//third_party/abseil-cpp/absl/algorithm:container", - "//third_party/abseil-cpp/absl/types:optional", - ] - } -} - rtc_library("rtc_data_sctp_transport_factory") { defines = [] sources = [ @@ -455,13 +449,11 @@ rtc_library("rtc_data_sctp_transport_factory") { ":rtc_data_sctp_transport_internal", "../api/transport:sctp_transport_factory_interface", "../rtc_base:threading", - "../rtc_base/experiments:field_trial_parser", "../rtc_base/system:unused", ] if (rtc_enable_sctp) { - assert(rtc_build_dcsctp || rtc_build_usrsctp, - "An SCTP backend is required to enable SCTP") + assert(rtc_build_dcsctp, "An SCTP backend is required to enable SCTP") } if (rtc_build_dcsctp) { @@ -472,11 +464,6 @@ rtc_library("rtc_data_sctp_transport_factory") { "../system_wrappers:field_trial", ] } - - if (rtc_build_usrsctp) { - defines += [ "WEBRTC_HAVE_USRSCTP" ] - deps += [ ":rtc_data_usrsctp_transport" ] - } } rtc_source_set("rtc_media") { @@ -516,14 +503,20 @@ if (rtc_include_tests) { "../modules/video_coding:video_coding_utility", "../p2p:rtc_p2p", "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:byte_order", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", + "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/third_party/sigslot", + "../test:scoped_key_value_config", "../test:test_support", "//testing/gtest", ] @@ -626,13 +619,18 @@ if (rtc_include_tests) { "../modules/video_coding/codecs/av1:libaom_av1_encoder_if_supported", "../p2p:p2p_test_utils", "../rtc_base", + "../rtc_base:byte_order", "../rtc_base:checks", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", + "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/experiments:min_video_bitrate_experiment", "../rtc_base/synchronization:mutex", "../rtc_base/third_party/sigslot", @@ -641,6 +639,7 @@ if (rtc_include_tests) { "../test:fake_video_codecs", "../test:field_trial", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_main", "../test:test_support", "../test:video_test_common", @@ -681,21 +680,6 @@ if (rtc_include_tests) { sources += [ "engine/webrtc_voice_engine_unittest.cc" ] } - if (rtc_build_usrsctp) { - sources += [ - "sctp/usrsctp_transport_reliability_unittest.cc", - "sctp/usrsctp_transport_unittest.cc", - ] - deps += [ - ":rtc_data_sctp_transport_internal", - ":rtc_data_usrsctp_transport", - "../rtc_base:rtc_event", - "../rtc_base/task_utils:pending_task_safety_flag", - "../rtc_base/task_utils:to_queued_task", - "//third_party/usrsctp", - ] - } - if (rtc_opus_support_120ms_ptime) { defines += [ "WEBRTC_OPUS_SUPPORT_120MS_PTIME=1" ] } else { @@ -712,6 +696,16 @@ if (rtc_include_tests) { if (is_ios) { deps += [ ":rtc_media_unittests_bundle_data" ] } + + if (rtc_build_dcsctp) { + sources += [ "sctp/dcsctp_transport_unittest.cc" ] + deps += [ + ":rtc_data_dcsctp_transport", + "../net/dcsctp/public:factory", + "../net/dcsctp/public:mocks", + "../net/dcsctp/public:socket", + ] + } } } } diff --git a/media/DEPS b/media/DEPS index 127e3ef081..a495631950 100644 --- a/media/DEPS +++ b/media/DEPS @@ -15,7 +15,6 @@ include_rules = [ "+p2p", "+sound", "+system_wrappers", - "+usrsctplib", "+third_party/libyuv", ] diff --git a/media/OWNERS b/media/OWNERS index b8910326b9..c822b8f34f 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -8,3 +8,6 @@ perkj@webrtc.org # Audio-related changes: peah@webrtc.org saza@webrtc.org + +# Datachannel-related changes: +orphis@webrtc.org diff --git a/media/base/codec.cc b/media/base/codec.cc index 9b09f5e73b..4935e94b25 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -18,7 +18,6 @@ #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" -#include "system_wrappers/include/field_trial.h" namespace cricket { namespace { @@ -129,13 +128,14 @@ bool Codec::operator==(const Codec& c) const { feedback_params == c.feedback_params; } -bool Codec::Matches(const Codec& codec) const { +bool Codec::Matches(const Codec& codec, + const webrtc::FieldTrialsView* field_trials) const { // Match the codec id/name based on the typical static/dynamic name rules. // Matching is case-insensitive. // Legacy behaviour with killswitch. - if (webrtc::field_trial::IsDisabled( - "WebRTC-PayloadTypes-Lower-Dynamic-Range")) { + if (field_trials && + field_trials->IsDisabled("WebRTC-PayloadTypes-Lower-Dynamic-Range")) { const int kMaxStaticPayloadId = 95; return (id <= kMaxStaticPayloadId || codec.id <= kMaxStaticPayloadId) ? (id == codec.id) @@ -238,7 +238,8 @@ bool AudioCodec::operator==(const AudioCodec& c) const { return bitrate == c.bitrate && channels == c.channels && Codec::operator==(c); } -bool AudioCodec::Matches(const AudioCodec& codec) const { +bool AudioCodec::Matches(const AudioCodec& codec, + const webrtc::FieldTrialsView* field_trials) const { // If a nonzero clockrate is specified, it must match the actual clockrate. // If a nonzero bitrate is specified, it must match the actual bitrate, // unless the codec is VBR (0), where we just force the supplied value. @@ -248,7 +249,7 @@ bool AudioCodec::Matches(const AudioCodec& codec) const { // omitted if the number of channels is one." // Preference is ignored. // TODO(juberti): Treat a zero clockrate as 8000Hz, the RTP default clockrate. - return Codec::Matches(codec) && + return Codec::Matches(codec, field_trials) && ((codec.clockrate == 0 /*&& clockrate == 8000*/) || clockrate == codec.clockrate) && (codec.bitrate == 0 || bitrate <= 0 || bitrate == codec.bitrate) && @@ -324,8 +325,9 @@ bool VideoCodec::operator==(const VideoCodec& c) const { return Codec::operator==(c) && packetization == c.packetization; } -bool VideoCodec::Matches(const VideoCodec& other) const { - return Codec::Matches(other) && +bool VideoCodec::Matches(const VideoCodec& other, + const webrtc::FieldTrialsView* field_trials) const { + return Codec::Matches(other, field_trials) && IsSameCodecSpecific(name, params, other.name, other.params); } diff --git a/media/base/codec.h b/media/base/codec.h index cfc31aed1f..c07cf60025 100644 --- a/media/base/codec.h +++ b/media/base/codec.h @@ -17,6 +17,7 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/video_codecs/sdp_video_format.h" #include "media/base/media_constants.h" @@ -75,7 +76,8 @@ struct RTC_EXPORT Codec { virtual ~Codec(); // Indicates if this codec is compatible with the specified codec. - bool Matches(const Codec& codec) const; + bool Matches(const Codec& codec, + const webrtc::FieldTrialsView* field_trials = nullptr) const; bool MatchesCapability(const webrtc::RtpCodecCapability& capability) const; // Find the parameter for `name` and write the value to `out`. @@ -132,7 +134,8 @@ struct AudioCodec : public Codec { ~AudioCodec() override = default; // Indicates if this codec is compatible with the specified codec. - bool Matches(const AudioCodec& codec) const; + bool Matches(const AudioCodec& codec, + const webrtc::FieldTrialsView* field_trials = nullptr) const; std::string ToString() const; @@ -163,7 +166,8 @@ struct RTC_EXPORT VideoCodec : public Codec { // Indicates if this video codec is the same as the other video codec, e.g. if // they are both VP8 or VP9, or if they are both H264 with the same H264 // profile. H264 levels however are not compared. - bool Matches(const VideoCodec& codec) const; + bool Matches(const VideoCodec& codec, + const webrtc::FieldTrialsView* field_trials = nullptr) const; std::string ToString() const; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 2b0ef81277..3673169939 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -137,6 +137,8 @@ struct VideoOptions { // Force screencast to use a minimum bitrate. This flag comes from // the PeerConnection constraint 'googScreencastMinBitrate'. It is // copied to the encoder config by WebRtcVideoChannel. + // TODO(https://crbug.com/1315155): Remove the ability to set it in Chromium + // and delete this flag (it should default to 100 kbps). absl::optional screencast_min_bitrate_kbps; // Set by screencast sources. Implies selection of encoding settings // suitable for screencast. Most likely not the right way to do @@ -465,7 +467,6 @@ struct VoiceSenderInfo : public MediaSenderInfo { // https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy double total_input_energy = 0.0; double total_input_duration = 0.0; - bool typing_noise_detected = false; webrtc::ANAStats ana_statistics; webrtc::AudioProcessingStats apm_statistics; }; @@ -612,6 +613,8 @@ struct VideoReceiverInfo : public MediaReceiverInfo { absl::optional qp_sum; // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totaldecodetime uint64_t total_decode_time_ms = 0; + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-totalprocessingdelay + webrtc::TimeDelta total_processing_delay = webrtc::TimeDelta::Millis(0); double total_inter_frame_delay = 0; double total_squared_inter_frame_delay = 0; int64_t interframe_delay_max_ms = -1; diff --git a/media/base/media_config.h b/media/base/media_config.h index be314a8aa3..b383c9aa3d 100644 --- a/media/base/media_config.h +++ b/media/base/media_config.h @@ -18,12 +18,16 @@ namespace cricket { struct MediaConfig { // Set DSCP value on packets. This flag comes from the // PeerConnection constraint 'googDscp'. - bool enable_dscp = false; + // TODO(https://crbug.com/1315574): Remove the ability to set it in Chromium + // and delete this flag. + bool enable_dscp = true; // Video-specific config. struct Video { // Enable WebRTC CPU Overuse Detection. This flag comes from the // PeerConnection constraint 'googCpuOveruseDetection'. + // TODO(https://crbug.com/1315569): Remove the ability to set it in Chromium + // and delete this flag. bool enable_cpu_adaptation = true; // Enable WebRTC suspension of video. No video frames will be sent @@ -31,6 +35,8 @@ struct MediaConfig { // flag comes from the PeerConnection constraint // 'googSuspendBelowMinBitrate', and WebRtcVideoChannel copies it // to VideoSendStream::Config::suspend_below_min_bitrate. + // TODO(https://crbug.com/1315564): Remove the ability to set it in Chromium + // and delete this flag. bool suspend_below_min_bitrate = false; // Enable buffering and playout timing smoothing of decoded frames. diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index 21c3787382..813657eb00 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -152,7 +152,7 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( } CompositeMediaEngine::CompositeMediaEngine( - std::unique_ptr trials, + std::unique_ptr trials, std::unique_ptr audio_engine, std::unique_ptr video_engine) : trials_(std::move(trials)), diff --git a/media/base/media_engine.h b/media/base/media_engine.h index 1778104a30..5a6a8c29a0 100644 --- a/media/base/media_engine.h +++ b/media/base/media_engine.h @@ -18,8 +18,8 @@ #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h" #include "api/crypto/crypto_options.h" +#include "api/field_trials_view.h" #include "api/rtp_parameters.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/video/video_bitrate_allocator_factory.h" #include "call/audio_state.h" #include "media/base/codec.h" @@ -137,10 +137,10 @@ class MediaEngineInterface { // CompositeMediaEngine constructs a MediaEngine from separate // voice and video engine classes. -// Optionally owns a WebRtcKeyValueConfig trials map. +// Optionally owns a FieldTrialsView trials map. class CompositeMediaEngine : public MediaEngineInterface { public: - CompositeMediaEngine(std::unique_ptr trials, + CompositeMediaEngine(std::unique_ptr trials, std::unique_ptr audio_engine, std::unique_ptr video_engine); CompositeMediaEngine(std::unique_ptr audio_engine, @@ -156,7 +156,7 @@ class CompositeMediaEngine : public MediaEngineInterface { const VideoEngineInterface& video() const override; private: - const std::unique_ptr trials_; + const std::unique_ptr trials_; const std::unique_ptr voice_engine_; const std::unique_ptr video_engine_; }; diff --git a/media/base/test_utils.h b/media/base/test_utils.h index 22bda4f12a..fb18485d32 100644 --- a/media/base/test_utils.h +++ b/media/base/test_utils.h @@ -37,10 +37,12 @@ inline std::vector MakeVector(const T a[], size_t s) { // Checks whether `codecs` contains `codec`; checks using Codec::Matches(). template -bool ContainsMatchingCodec(const std::vector& codecs, const C& codec) { +bool ContainsMatchingCodec(const std::vector& codecs, + const C& codec, + const webrtc::FieldTrialsView* field_trials) { typename std::vector::const_iterator it; for (it = codecs.begin(); it != codecs.end(); ++it) { - if (it->Matches(codec)) { + if (it->Matches(codec, field_trials)) { return true; } } diff --git a/media/base/video_adapter.cc b/media/base/video_adapter.cc index 4785dfcfe2..bad40b8988 100644 --- a/media/base/video_adapter.cc +++ b/media/base/video_adapter.cc @@ -132,7 +132,7 @@ VideoAdapter::VideoAdapter(int source_resolution_alignment) adaption_changes_(0), previous_width_(0), previous_height_(0), - variable_start_scale_factor_(webrtc::field_trial::IsEnabled( + variable_start_scale_factor_(!webrtc::field_trial::IsDisabled( "WebRTC-Video-VariableStartScaleFactor")), source_resolution_alignment_(source_resolution_alignment), resolution_alignment_(source_resolution_alignment), diff --git a/media/base/video_adapter_unittest.cc b/media/base/video_adapter_unittest.cc index 347e24d905..af3585916e 100644 --- a/media/base/video_adapter_unittest.cc +++ b/media/base/video_adapter_unittest.cc @@ -147,21 +147,10 @@ class VideoAdapterTest : public ::testing::Test, const bool use_new_format_request_; }; -class VideoAdapterTestVariableStartScale : public VideoAdapterTest { - public: - VideoAdapterTestVariableStartScale() - : VideoAdapterTest("WebRTC-Video-VariableStartScaleFactor/Enabled/", - /*source_resolution_alignment=*/1) {} -}; - INSTANTIATE_TEST_SUITE_P(OnOutputFormatRequests, VideoAdapterTest, ::testing::Values(true, false)); -INSTANTIATE_TEST_SUITE_P(OnOutputFormatRequests, - VideoAdapterTestVariableStartScale, - ::testing::Values(true, false)); - // Do not adapt the frame rate or the resolution. Expect no frame drop, no // cropping, and no resolution change. TEST_P(VideoAdapterTest, AdaptNothing) { @@ -955,8 +944,8 @@ TEST_P(VideoAdapterTest, TestAdaptToVerySmallResolution) { EXPECT_TRUE(adapter_.AdaptFrameResolution( w, h, 0, &cropped_width_, &cropped_height_, &out_width_, &out_height_)); - EXPECT_EQ(180, out_width_); - EXPECT_EQ(99, out_height_); + EXPECT_EQ(160, out_width_); + EXPECT_EQ(90, out_height_); } TEST_P(VideoAdapterTest, AdaptFrameResolutionDropWithResolutionRequest) { @@ -1053,40 +1042,7 @@ TEST(VideoAdapterTestMultipleOrientation, TestForcePortrait) { EXPECT_EQ(640, out_height); } -TEST_P(VideoAdapterTest, AdaptResolutionInSteps) { - const int kWidth = 1280; - const int kHeight = 720; - OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 16:9 aspect. - - // Scale factors: 3/4, 2/3, 3/4, 2/3, ... - // Scale : 3/4, 1/2, 3/8, 1/4, 3/16, 1/8. - const int kExpectedWidths[] = {960, 640, 480, 320, 240, 160}; - const int kExpectedHeights[] = {540, 360, 270, 180, 135, 90}; - - int request_width = kWidth; - int request_height = kHeight; - - for (size_t i = 0; i < arraysize(kExpectedWidths); ++i) { - // Adapt down one step. - adapter_.OnSinkWants(BuildSinkWants(absl::nullopt, - request_width * request_height - 1, - std::numeric_limits::max())); - EXPECT_TRUE(adapter_.AdaptFrameResolution(kWidth, kHeight, 0, - &cropped_width_, &cropped_height_, - &out_width_, &out_height_)); - EXPECT_EQ(kExpectedWidths[i], out_width_); - EXPECT_EQ(kExpectedHeights[i], out_height_); - request_width = out_width_; - request_height = out_height_; - } -} - -// Scale factors are 3/4, 2/3, 3/4, 2/3, ... (see test above). -// In VideoAdapterTestVariableStartScale, first scale factor depends on -// resolution. May start with: -// - 2/3 (if width/height multiple of 3) or -// - 2/3, 2/3 (if width/height multiple of 9). -TEST_P(VideoAdapterTestVariableStartScale, AdaptResolutionInStepsFirst3_4) { +TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst3_4) { const int kWidth = 1280; const int kHeight = 720; OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 16:9 aspect. @@ -1114,7 +1070,7 @@ TEST_P(VideoAdapterTestVariableStartScale, AdaptResolutionInStepsFirst3_4) { } } -TEST_P(VideoAdapterTestVariableStartScale, AdaptResolutionInStepsFirst2_3) { +TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst2_3) { const int kWidth = 1920; const int kHeight = 1080; OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 16:9 aspect. @@ -1142,7 +1098,7 @@ TEST_P(VideoAdapterTestVariableStartScale, AdaptResolutionInStepsFirst2_3) { } } -TEST_P(VideoAdapterTestVariableStartScale, AdaptResolutionInStepsFirst2x2_3) { +TEST_P(VideoAdapterTest, AdaptResolutionInStepsFirst2x2_3) { const int kWidth = 1440; const int kHeight = 1080; OnOutputFormatRequest(kWidth, kHeight, absl::nullopt); // 4:3 aspect. diff --git a/media/base/video_broadcaster_unittest.cc b/media/base/video_broadcaster_unittest.cc index b9672375a7..fe1a1cb725 100644 --- a/media/base/video_broadcaster_unittest.cc +++ b/media/base/video_broadcaster_unittest.cc @@ -66,7 +66,7 @@ TEST(VideoBroadcasterTest, OnFrame) { rtc::scoped_refptr buffer( webrtc::I420Buffer::Create(kWidth, kHeight)); // Initialize, to avoid warnings on use of initialized values. - webrtc::I420Buffer::SetBlack(buffer); + webrtc::I420Buffer::SetBlack(buffer.get()); webrtc::VideoFrame frame = webrtc::VideoFrame::Builder() .set_video_frame_buffer(buffer) diff --git a/media/engine/fake_webrtc_call.cc b/media/engine/fake_webrtc_call.cc index 7b9174d961..19a4ad2199 100644 --- a/media/engine/fake_webrtc_call.cc +++ b/media/engine/fake_webrtc_call.cc @@ -130,6 +130,16 @@ void FakeAudioReceiveStream::SetRtpExtensions( config_.rtp.extensions = std::move(extensions); } +const std::vector& +FakeAudioReceiveStream::GetRtpExtensions() const { + return config_.rtp.extensions; +} + +webrtc::RtpHeaderExtensionMap FakeAudioReceiveStream::GetRtpExtensionMap() + const { + return webrtc::RtpHeaderExtensionMap(config_.rtp.extensions); +} + webrtc::AudioReceiveStream::Stats FakeAudioReceiveStream::GetStats( bool get_and_clear_legacy_stats) const { return stats_; @@ -259,7 +269,7 @@ void FakeVideoSendStream::ReconfigureVideoEncoder( } video_streams_ = config.video_stream_factory->CreateEncoderStreams(width, height, config); - if (config.encoder_specific_settings != NULL) { + if (config.encoder_specific_settings != nullptr) { const unsigned char num_temporal_layers = static_cast( video_streams_.back().num_temporal_layers.value_or(1)); if (config_.rtp.payload_name == "VP8") { @@ -286,7 +296,7 @@ void FakeVideoSendStream::ReconfigureVideoEncoder( << config_.rtp.payload_name; } } - codec_settings_set_ = config.encoder_specific_settings != NULL; + codec_settings_set_ = config.encoder_specific_settings != nullptr; encoder_config_ = std::move(config); ++num_encoder_reconfigurations_; } @@ -380,6 +390,11 @@ void FakeVideoReceiveStream::SetRtpExtensions( config_.rtp.extensions = std::move(extensions); } +webrtc::RtpHeaderExtensionMap FakeVideoReceiveStream::GetRtpExtensionMap() + const { + return webrtc::RtpHeaderExtensionMap(config_.rtp.extensions); +} + void FakeVideoReceiveStream::Start() { receiving_ = true; } @@ -394,14 +409,19 @@ void FakeVideoReceiveStream::SetStats( } FakeFlexfecReceiveStream::FakeFlexfecReceiveStream( - const webrtc::FlexfecReceiveStream::Config& config) - : config_(config) {} + const webrtc::FlexfecReceiveStream::Config config) + : config_(std::move(config)) {} void FakeFlexfecReceiveStream::SetRtpExtensions( std::vector extensions) { config_.rtp.extensions = std::move(extensions); } +webrtc::RtpHeaderExtensionMap FakeFlexfecReceiveStream::GetRtpExtensionMap() + const { + return webrtc::RtpHeaderExtensionMap(config_.rtp.extensions); +} + const webrtc::FlexfecReceiveStream::Config& FakeFlexfecReceiveStream::GetConfig() const { return config_; @@ -416,17 +436,19 @@ void FakeFlexfecReceiveStream::OnRtpPacket(const webrtc::RtpPacketReceived&) { RTC_DCHECK_NOTREACHED() << "Not implemented."; } -FakeCall::FakeCall() - : FakeCall(rtc::Thread::Current(), rtc::Thread::Current()) {} +FakeCall::FakeCall(webrtc::test::ScopedKeyValueConfig* field_trials) + : FakeCall(rtc::Thread::Current(), rtc::Thread::Current(), field_trials) {} FakeCall::FakeCall(webrtc::TaskQueueBase* worker_thread, - webrtc::TaskQueueBase* network_thread) + webrtc::TaskQueueBase* network_thread, + webrtc::test::ScopedKeyValueConfig* field_trials) : network_thread_(network_thread), worker_thread_(worker_thread), audio_network_state_(webrtc::kNetworkUp), video_network_state_(webrtc::kNetworkUp), num_created_send_streams_(0), - num_created_receive_streams_(0) {} + num_created_receive_streams_(0), + trials_(field_trials ? field_trials : &fallback_trials_) {} FakeCall::~FakeCall() { EXPECT_EQ(0u, video_send_streams_.size()); @@ -583,8 +605,9 @@ void FakeCall::DestroyVideoReceiveStream( } webrtc::FlexfecReceiveStream* FakeCall::CreateFlexfecReceiveStream( - const webrtc::FlexfecReceiveStream::Config& config) { - FakeFlexfecReceiveStream* fake_stream = new FakeFlexfecReceiveStream(config); + const webrtc::FlexfecReceiveStream::Config config) { + FakeFlexfecReceiveStream* fake_stream = + new FakeFlexfecReceiveStream(std::move(config)); flexfec_receive_streams_.push_back(fake_stream); ++num_created_receive_streams_; return fake_stream; diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h index b84c385b9c..c26f7b1ddf 100644 --- a/media/engine/fake_webrtc_call.h +++ b/media/engine/fake_webrtc_call.h @@ -36,6 +36,7 @@ #include "call/video_send_stream.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/buffer.h" +#include "test/scoped_key_value_config.h" namespace cricket { class FakeAudioSendStream final : public webrtc::AudioSendStream { @@ -109,9 +110,8 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream { } private: - const webrtc::ReceiveStream::RtpConfig& rtp_config() const override { - return config_.rtp; - } + bool transport_cc() const override { return config_.rtp.transport_cc; } + uint32_t remote_ssrc() const override { return config_.rtp.remote_ssrc; } void Start() override { started_ = true; } void Stop() override { started_ = false; } bool IsRunning() const override { return started_; } @@ -126,6 +126,8 @@ class FakeAudioReceiveStream final : public webrtc::AudioReceiveStream { void SetFrameDecryptor(rtc::scoped_refptr frame_decryptor) override; void SetRtpExtensions(std::vector extensions) override; + const std::vector& GetRtpExtensions() const override; + webrtc::RtpHeaderExtensionMap GetRtpExtensionMap() const override; webrtc::AudioReceiveStream::Stats GetStats( bool get_and_clear_legacy_stats) const override; @@ -264,10 +266,8 @@ class FakeVideoReceiveStream final : public webrtc::VideoReceiveStream { private: // webrtc::VideoReceiveStream implementation. void SetRtpExtensions(std::vector extensions) override; - - const webrtc::ReceiveStream::RtpConfig& rtp_config() const override { - return config_.rtp; - } + webrtc::RtpHeaderExtensionMap GetRtpExtensionMap() const override; + bool transport_cc() const override { return config_.rtp.transport_cc; } void Start() override; void Stop() override; @@ -293,16 +293,16 @@ class FakeVideoReceiveStream final : public webrtc::VideoReceiveStream { class FakeFlexfecReceiveStream final : public webrtc::FlexfecReceiveStream { public: explicit FakeFlexfecReceiveStream( - const webrtc::FlexfecReceiveStream::Config& config); + const webrtc::FlexfecReceiveStream::Config config); void SetRtpExtensions(std::vector extensions) override; - - const webrtc::ReceiveStream::RtpConfig& rtp_config() const override { - return config_.rtp; - } + webrtc::RtpHeaderExtensionMap GetRtpExtensionMap() const override; + bool transport_cc() const override { return config_.rtp.transport_cc; } const webrtc::FlexfecReceiveStream::Config& GetConfig() const; + uint32_t remote_ssrc() const { return config_.rtp.remote_ssrc; } + private: webrtc::FlexfecReceiveStream::Stats GetStats() const override; @@ -313,9 +313,10 @@ class FakeFlexfecReceiveStream final : public webrtc::FlexfecReceiveStream { class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { public: - FakeCall(); + explicit FakeCall(webrtc::test::ScopedKeyValueConfig* field_trials = nullptr); FakeCall(webrtc::TaskQueueBase* worker_thread, - webrtc::TaskQueueBase* network_thread); + webrtc::TaskQueueBase* network_thread, + webrtc::test::ScopedKeyValueConfig* field_trials = nullptr); ~FakeCall() override; webrtc::MockRtpTransportControllerSend* GetMockTransportControllerSend() { @@ -353,6 +354,13 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { void SetClientBitratePreferences( const webrtc::BitrateSettings& preferences) override {} + void SetFieldTrial(const std::string& field_trial_string) { + trials_overrides_ = std::make_unique( + *trials_, field_trial_string); + } + + const webrtc::FieldTrialsView& trials() const override { return *trials_; } + private: webrtc::AudioSendStream* CreateAudioSendStream( const webrtc::AudioSendStream::Config& config) override; @@ -374,7 +382,7 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { webrtc::VideoReceiveStream* receive_stream) override; webrtc::FlexfecReceiveStream* CreateFlexfecReceiveStream( - const webrtc::FlexfecReceiveStream::Config& config) override; + const webrtc::FlexfecReceiveStream::Config config) override; void DestroyFlexfecReceiveStream( webrtc::FlexfecReceiveStream* receive_stream) override; @@ -394,10 +402,6 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { webrtc::Call::Stats GetStats() const override; - const webrtc::WebRtcKeyValueConfig& trials() const override { - return trials_; - } - webrtc::TaskQueueBase* network_thread() const override; webrtc::TaskQueueBase* worker_thread() const override; @@ -432,7 +436,16 @@ class FakeCall final : public webrtc::Call, public webrtc::PacketReceiver { int num_created_send_streams_; int num_created_receive_streams_; - webrtc::FieldTrialBasedConfig trials_; + + // The field trials that are in use, either supplied by caller + // or pointer to &fallback_trials_. + webrtc::test::ScopedKeyValueConfig* trials_; + + // fallback_trials_ is used if caller does not provide any field trials. + webrtc::test::ScopedKeyValueConfig fallback_trials_; + + // An extra field trial that can be set using SetFieldTrial. + std::unique_ptr trials_overrides_; }; } // namespace cricket diff --git a/media/engine/internal_encoder_factory.cc b/media/engine/internal_encoder_factory.cc index 5ccb93d0c1..03d51a5526 100644 --- a/media/engine/internal_encoder_factory.cc +++ b/media/engine/internal_encoder_factory.cc @@ -16,10 +16,13 @@ #include "api/video_codecs/sdp_video_format.h" #include "media/base/codec.h" #include "media/base/media_constants.h" +#include "modules/video_coding/codecs/av1/av1_svc_config.h" #include "modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h" #include "modules/video_coding/codecs/h264/include/h264.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" +#include "modules/video_coding/codecs/vp8/vp8_scalability.h" #include "modules/video_coding/codecs/vp9/include/vp9.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "rtc_base/logging.h" namespace webrtc { @@ -59,36 +62,40 @@ std::unique_ptr InternalEncoderFactory::CreateVideoEncoder( VideoEncoderFactory::CodecSupport InternalEncoderFactory::QueryCodecSupport( const SdpVideoFormat& format, - absl::optional scalability_mode) const { + absl::optional scalability_mode_string) const { // Query for supported formats and check if the specified format is supported. // Begin with filtering out unsupported scalability modes. - if (scalability_mode) { - bool scalability_mode_supported = false; + if (scalability_mode_string) { + static constexpr VideoEncoderFactory::CodecSupport kUnsupported = { + .is_supported = false, .is_power_efficient = false}; + absl::optional scalability_mode = + ScalabilityModeFromString(*scalability_mode_string); + if (!scalability_mode.has_value()) { + return kUnsupported; + } if (absl::EqualsIgnoreCase(format.name, cricket::kVp8CodecName)) { - scalability_mode_supported = - VP8Encoder::SupportsScalabilityMode(*scalability_mode); + if (!VP8SupportsScalabilityMode(*scalability_mode)) { + return kUnsupported; + } } else if (absl::EqualsIgnoreCase(format.name, cricket::kVp9CodecName)) { - scalability_mode_supported = - VP9Encoder::SupportsScalabilityMode(*scalability_mode); + if (!VP9Encoder::SupportsScalabilityMode(*scalability_mode)) { + return kUnsupported; + } } else if (absl::EqualsIgnoreCase(format.name, cricket::kH264CodecName)) { - scalability_mode_supported = - H264Encoder::SupportsScalabilityMode(*scalability_mode); + if (!H264Encoder::SupportsScalabilityMode(*scalability_mode)) { + return kUnsupported; + } } else if (kIsLibaomAv1EncoderSupported && absl::EqualsIgnoreCase(format.name, cricket::kAv1CodecName)) { - scalability_mode_supported = - LibaomAv1EncoderSupportsScalabilityMode(*scalability_mode); - } - - static constexpr VideoEncoderFactory::CodecSupport kUnsupported = { - /*is_supported=*/false, /*is_power_efficient=*/false}; - if (!scalability_mode_supported) { + if (!LibaomAv1EncoderSupportsScalabilityMode(*scalability_mode)) { + return kUnsupported; + } + } else { return kUnsupported; } } - CodecSupport codec_support; - codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); - return codec_support; + return {.is_supported = format.IsCodecInList(GetSupportedFormats())}; } } // namespace webrtc diff --git a/media/engine/null_webrtc_video_engine_unittest.cc b/media/engine/null_webrtc_video_engine_unittest.cc index a23a3b6cdf..9515d44be9 100644 --- a/media/engine/null_webrtc_video_engine_unittest.cc +++ b/media/engine/null_webrtc_video_engine_unittest.cc @@ -34,7 +34,7 @@ TEST(NullWebRtcVideoEngineTest, CheckInterface) { webrtc::test::MockAudioDeviceModule::CreateNice(); webrtc::FieldTrialBasedConfig trials; auto audio_engine = std::make_unique( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, webrtc::AudioProcessingBuilder().Create(), nullptr, trials); diff --git a/media/engine/simulcast.cc b/media/engine/simulcast.cc index 3909685995..93607e8c18 100644 --- a/media/engine/simulcast.cc +++ b/media/engine/simulcast.cc @@ -45,8 +45,7 @@ constexpr char kUseLegacySimulcastLayerLimitFieldTrial[] = constexpr double kDefaultMaxRoundupRate = 0.1; // TODO(webrtc:12415): Flip this to a kill switch when this feature launches. -bool EnableLowresBitrateInterpolation( - const webrtc::WebRtcKeyValueConfig& trials) { +bool EnableLowresBitrateInterpolation(const webrtc::FieldTrialsView& trials) { return absl::StartsWith( trials.Lookup("WebRTC-LowresSimulcastBitrateInterpolation"), "Enabled"); } @@ -134,7 +133,7 @@ const int kMaxScreenshareSimulcastLayers = 2; // Multiway: Number of temporal layers for each simulcast stream. int DefaultNumberOfTemporalLayers(int simulcast_id, bool screenshare, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { RTC_CHECK_GE(simulcast_id, 0); RTC_CHECK_LT(simulcast_id, webrtc::kMaxSimulcastStreams); @@ -291,7 +290,7 @@ size_t LimitSimulcastLayerCount(int width, int height, size_t need_layers, size_t layer_count, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { if (!absl::StartsWith(trials.Lookup(kUseLegacySimulcastLayerLimitFieldTrial), "Disabled")) { // Max layers from one higher resolution in kSimulcastFormats will be used @@ -327,7 +326,7 @@ std::vector GetSimulcastConfig( int max_qp, bool is_screenshare_with_conference_mode, bool temporal_layers_supported, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { RTC_DCHECK_LE(min_layers, max_layers); RTC_DCHECK(max_layers > 1 || is_screenshare_with_conference_mode); @@ -359,7 +358,7 @@ std::vector GetNormalSimulcastLayers( int max_qp, bool temporal_layers_supported, bool base_heavy_tl3_rate_alloc, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { std::vector layers(layer_count); const bool enable_lowres_bitrate_interpolation = @@ -451,7 +450,7 @@ std::vector GetScreenshareLayers( int max_qp, bool temporal_layers_supported, bool base_heavy_tl3_rate_alloc, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { auto max_screenshare_layers = kMaxScreenshareSimulcastLayers; size_t num_simulcast_layers = std::min(max_layers, max_screenshare_layers); diff --git a/media/engine/simulcast.h b/media/engine/simulcast.h index aa8c394816..e367830889 100644 --- a/media/engine/simulcast.h +++ b/media/engine/simulcast.h @@ -15,7 +15,7 @@ #include -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/units/data_rate.h" #include "api/video_codecs/video_encoder_config.h" @@ -43,7 +43,7 @@ std::vector GetSimulcastConfig( int max_qp, bool is_screenshare_with_conference_mode, bool temporal_layers_supported, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); // Gets the simulcast config layers for a non-screensharing case. std::vector GetNormalSimulcastLayers( @@ -54,7 +54,7 @@ std::vector GetNormalSimulcastLayers( int max_qp, bool temporal_layers_supported, bool base_heavy_tl3_rate_alloc, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); // Gets simulcast config layers for screenshare settings. std::vector GetScreenshareLayers( @@ -65,7 +65,7 @@ std::vector GetScreenshareLayers( int max_qp, bool temporal_layers_supported, bool base_heavy_tl3_rate_alloc, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); } // namespace cricket diff --git a/media/engine/simulcast_encoder_adapter.cc b/media/engine/simulcast_encoder_adapter.cc index 9143361e82..eeabff2c88 100644 --- a/media/engine/simulcast_encoder_adapter.cc +++ b/media/engine/simulcast_encoder_adapter.cc @@ -791,8 +791,8 @@ webrtc::VideoCodec SimulcastEncoderAdapter::MakeStreamCodec( // kComplexityHigher, which maps to cpu_used = -4. int pixels_per_frame = codec_params.width * codec_params.height; if (pixels_per_frame < 352 * 288) { - codec_params.VP8()->complexity = - webrtc::VideoCodecComplexity::kComplexityHigher; + codec_params.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); } // Turn off denoising for all streams but the highest resolution. codec_params.VP8()->denoisingOn = false; @@ -824,7 +824,9 @@ void SimulcastEncoderAdapter::OverrideFromFieldTrial( info->apply_alignment_to_all_simulcast_layers || encoder_info_override_.apply_alignment_to_all_simulcast_layers(); } - if (!encoder_info_override_.resolution_bitrate_limits().empty()) { + // Override resolution bitrate limits unless they're set already. + if (info->resolution_bitrate_limits.empty() && + !encoder_info_override_.resolution_bitrate_limits().empty()) { info->resolution_bitrate_limits = encoder_info_override_.resolution_bitrate_limits(); } diff --git a/media/engine/simulcast_encoder_adapter_unittest.cc b/media/engine/simulcast_encoder_adapter_unittest.cc index bb2335cb5b..c18df46632 100644 --- a/media/engine/simulcast_encoder_adapter_unittest.cc +++ b/media/engine/simulcast_encoder_adapter_unittest.cc @@ -182,6 +182,10 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { void set_supports_simulcast(bool supports_simulcast) { supports_simulcast_ = supports_simulcast; } + void set_resolution_bitrate_limits( + std::vector limits) { + resolution_bitrate_limits_ = limits; + } void DestroyVideoEncoder(VideoEncoder* encoder); @@ -193,6 +197,7 @@ class MockVideoEncoderFactory : public VideoEncoderFactory { // Keep number of entries in sync with `kMaxSimulcastStreams`. std::vector requested_resolution_alignments_ = {1, 1, 1}; bool supports_simulcast_ = false; + std::vector resolution_bitrate_limits_; }; class MockVideoEncoder : public VideoEncoder { @@ -245,6 +250,7 @@ class MockVideoEncoder : public VideoEncoder { info.fps_allocation[0] = fps_allocation_; info.supports_simulcast = supports_simulcast_; info.is_qp_trusted = is_qp_trusted_; + info.resolution_bitrate_limits = resolution_bitrate_limits; return info; } @@ -312,6 +318,11 @@ class MockVideoEncoder : public VideoEncoder { is_qp_trusted_ = is_qp_trusted; } + void set_resolution_bitrate_limits( + std::vector limits) { + resolution_bitrate_limits = limits; + } + bool supports_simulcast() const { return supports_simulcast_; } SdpVideoFormat video_format() const { return video_format_; } @@ -331,6 +342,7 @@ class MockVideoEncoder : public VideoEncoder { bool supports_simulcast_ = false; absl::optional is_qp_trusted_; SdpVideoFormat video_format_; + std::vector resolution_bitrate_limits; VideoCodec codec_; EncodedImageCallback* callback_; @@ -359,6 +371,7 @@ std::unique_ptr MockVideoEncoderFactory::CreateVideoEncoder( requested_resolution_alignments_[encoders_.size()]); encoder->set_supports_simulcast(supports_simulcast_); encoder->set_video_format(format); + encoder->set_resolution_bitrate_limits(resolution_bitrate_limits_); encoders_.push_back(encoder.get()); return encoder; } @@ -502,7 +515,8 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, EXPECT_EQ(ref.maxBitrate, target.maxBitrate); EXPECT_EQ(ref.minBitrate, target.minBitrate); EXPECT_EQ(ref.maxFramerate, target.maxFramerate); - EXPECT_EQ(ref.VP8().complexity, target.VP8().complexity); + EXPECT_EQ(ref.GetVideoEncoderComplexity(), + target.GetVideoEncoderComplexity()); EXPECT_EQ(ref.VP8().numberOfTemporalLayers, target.VP8().numberOfTemporalLayers); EXPECT_EQ(ref.VP8().denoisingOn, target.VP8().denoisingOn); @@ -538,8 +552,8 @@ class TestSimulcastEncoderAdapterFake : public ::testing::Test, // stream 0, the lowest resolution stream. InitRefCodec(0, &ref_codec); ref_codec.qpMax = 45; - ref_codec.VP8()->complexity = - webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); @@ -1134,7 +1148,8 @@ TEST_F(TestSimulcastEncoderAdapterFake, DoesNotAlterMaxQpForScreenshare) { VideoCodec ref_codec; InitRefCodec(0, &ref_codec); ref_codec.qpMax = kHighMaxQp; - ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); @@ -1167,7 +1182,8 @@ TEST_F(TestSimulcastEncoderAdapterFake, VideoCodec ref_codec; InitRefCodec(2, &ref_codec, true /* reverse_layer_order */); ref_codec.qpMax = kHighMaxQp; - ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; + ref_codec.SetVideoEncoderComplexity( + webrtc::VideoCodecComplexity::kComplexityHigher); ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 2); @@ -1326,6 +1342,36 @@ TEST_F(TestSimulcastEncoderAdapterFake, adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); } +TEST_F( + TestSimulcastEncoderAdapterFake, + EncoderInfoFromFieldTrialDoesNotOverrideExistingBitrateLimitsInSinglecast) { + 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/"); + + std::vector bitrate_limits; + bitrate_limits.push_back( + VideoEncoder::ResolutionBitrateLimits(111, 11100, 44400, 77700)); + bitrate_limits.push_back( + VideoEncoder::ResolutionBitrateLimits(444, 22200, 55500, 88700)); + bitrate_limits.push_back( + VideoEncoder::ResolutionBitrateLimits(777, 33300, 66600, 99900)); + SetUp(); + helper_->factory()->set_resolution_bitrate_limits(bitrate_limits); + + SimulcastTestFixtureImpl::DefaultSettings( + &codec_, static_cast(kTestTemporalLayerProfile), + kVideoCodecVP8); + codec_.numberOfSimulcastStreams = 1; + EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); + ASSERT_EQ(1u, helper_->factory()->encoders().size()); + EXPECT_EQ(adapter_->GetEncoderInfo().resolution_bitrate_limits, + bitrate_limits); +} + TEST_F(TestSimulcastEncoderAdapterFake, EncoderInfoFromFieldTrial) { test::ScopedFieldTrials field_trials( "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/" diff --git a/media/engine/unhandled_packets_buffer.cc b/media/engine/unhandled_packets_buffer.cc index cb6f0ec335..563712bdf3 100644 --- a/media/engine/unhandled_packets_buffer.cc +++ b/media/engine/unhandled_packets_buffer.cc @@ -46,7 +46,6 @@ void UnhandledPacketsBuffer::BackfillPackets( start = insert_pos_; } - size_t count = 0; std::vector remaining; remaining.reserve(kMaxStashedPackets); for (size_t i = 0; i < buffer_.size(); ++i) { @@ -56,7 +55,6 @@ void UnhandledPacketsBuffer::BackfillPackets( // scheme. const uint32_t ssrc = buffer_[pos].ssrc; if (absl::c_linear_search(ssrcs, ssrc)) { - ++count; consumer(ssrc, buffer_[pos].packet_time_us, buffer_[pos].packet); } else { remaining.push_back(buffer_[pos]); diff --git a/media/engine/webrtc_media_engine.cc b/media/engine/webrtc_media_engine.cc index f083b9c9ca..d0d6de2034 100644 --- a/media/engine/webrtc_media_engine.cc +++ b/media/engine/webrtc_media_engine.cc @@ -30,12 +30,12 @@ std::unique_ptr CreateMediaEngine( MediaEngineDependencies dependencies) { // TODO(sprang): Make populating `dependencies.trials` mandatory and remove // these fallbacks. - std::unique_ptr fallback_trials( + std::unique_ptr fallback_trials( dependencies.trials ? nullptr : new webrtc::FieldTrialBasedConfig()); - const webrtc::WebRtcKeyValueConfig& trials = + const webrtc::FieldTrialsView& trials = dependencies.trials ? *dependencies.trials : *fallback_trials; auto audio_engine = std::make_unique( - dependencies.task_queue_factory, std::move(dependencies.adm), + dependencies.task_queue_factory, dependencies.adm.get(), std::move(dependencies.audio_encoder_factory), std::move(dependencies.audio_decoder_factory), std::move(dependencies.audio_mixer), @@ -137,7 +137,7 @@ std::vector FilterRtpExtensions( const std::vector& extensions, bool (*supported)(absl::string_view), bool filter_redundant_extensions, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { // Don't check against old parameters; this should have been done earlier. RTC_DCHECK(ValidateRtpExtensions(extensions, {})); RTC_DCHECK(supported); diff --git a/media/engine/webrtc_media_engine.h b/media/engine/webrtc_media_engine.h index ff977609b2..27d6f34c2f 100644 --- a/media/engine/webrtc_media_engine.h +++ b/media/engine/webrtc_media_engine.h @@ -19,11 +19,11 @@ #include "api/audio/audio_mixer.h" #include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_encoder_factory.h" +#include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/task_queue/task_queue_factory.h" #include "api/transport/bitrate_settings.h" #include "api/transport/field_trial_based_config.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/video_codecs/video_decoder_factory.h" #include "api/video_codecs/video_encoder_factory.h" #include "media/base/codec.h" @@ -53,7 +53,7 @@ struct MediaEngineDependencies { std::unique_ptr video_encoder_factory; std::unique_ptr video_decoder_factory; - const webrtc::WebRtcKeyValueConfig* trials = nullptr; + const webrtc::FieldTrialsView* trials = nullptr; }; // CreateMediaEngine may be called on any thread, though the engine is @@ -76,7 +76,7 @@ std::vector FilterRtpExtensions( const std::vector& extensions, bool (*supported)(absl::string_view), bool filter_redundant_extensions, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); webrtc::BitrateConstraints GetBitrateConfigForCodec(const Codec& codec); diff --git a/media/engine/webrtc_media_engine_unittest.cc b/media/engine/webrtc_media_engine_unittest.cc index 81982fae2b..79efea4e9c 100644 --- a/media/engine/webrtc_media_engine_unittest.cc +++ b/media/engine/webrtc_media_engine_unittest.cc @@ -13,10 +13,9 @@ #include #include -#include "api/transport/field_trial_based_config.h" #include "media/engine/webrtc_media_engine_defaults.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using webrtc::RtpExtension; @@ -129,7 +128,7 @@ TEST(WebRtcMediaEngineTest, ValidateRtpExtensionsChangeIdForUrl) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsEmptyList) { std::vector extensions; - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions1, true, trials); EXPECT_EQ(0u, filtered.size()); @@ -137,7 +136,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsEmptyList) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsIncludeOnlySupported) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions1, false, trials); EXPECT_EQ(2u, filtered.size()); @@ -147,7 +146,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsIncludeOnlySupported) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName1) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, false, trials); EXPECT_EQ(12u, filtered.size()); @@ -156,7 +155,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName1) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName2) { std::vector extensions = MakeUniqueExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(12u, filtered.size()); @@ -165,7 +164,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsSortedByName2) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsDontRemoveRedundant) { std::vector extensions = MakeRedundantExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, false, trials); EXPECT_EQ(12u, filtered.size()); @@ -175,7 +174,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsDontRemoveRedundant) { TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundant) { std::vector extensions = MakeRedundantExtensions(); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(6u, filtered.size()); @@ -189,7 +188,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantEncrypted1) { extensions.push_back(webrtc::RtpExtension("b", 2, true)); extensions.push_back(webrtc::RtpExtension("c", 3)); extensions.push_back(webrtc::RtpExtension("b", 4)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -206,7 +205,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantEncrypted2) { extensions.push_back(webrtc::RtpExtension("b", 2)); extensions.push_back(webrtc::RtpExtension("c", 3)); extensions.push_back(webrtc::RtpExtension("b", 4)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -218,9 +217,8 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantEncrypted2) { } TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe1) { - webrtc::test::ScopedFieldTrials override_field_trials_( + webrtc::test::ScopedKeyValueConfig trials( "WebRTC-FilterAbsSendTimeExtension/Enabled/"); - webrtc::FieldTrialBasedConfig trials; std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -245,7 +243,7 @@ TEST(WebRtcMediaEngineTest, extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 1)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(2u, filtered.size()); @@ -254,9 +252,8 @@ TEST(WebRtcMediaEngineTest, } TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBweEncrypted1) { - webrtc::test::ScopedFieldTrials override_field_trials_( + webrtc::test::ScopedKeyValueConfig trials( "WebRTC-FilterAbsSendTimeExtension/Enabled/"); - webrtc::FieldTrialBasedConfig trials; std::vector extensions; extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 3)); @@ -291,7 +288,7 @@ TEST(WebRtcMediaEngineTest, extensions.push_back( RtpExtension(RtpExtension::kTransportSequenceNumberUri, 2, true)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(3u, filtered.size()); @@ -306,7 +303,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe2) { extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 1)); extensions.push_back(RtpExtension(RtpExtension::kAbsSendTimeUri, 14)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 7)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(1u, filtered.size()); @@ -317,7 +314,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe3) { std::vector extensions; extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 2)); extensions.push_back(RtpExtension(RtpExtension::kTimestampOffsetUri, 14)); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; std::vector filtered = FilterRtpExtensions(extensions, SupportedExtensions2, true, trials); EXPECT_EQ(1u, filtered.size()); @@ -327,7 +324,7 @@ TEST(WebRtcMediaEngineTest, FilterRtpExtensionsRemoveRedundantBwe3) { TEST(WebRtcMediaEngineTest, Create) { MediaEngineDependencies deps; webrtc::SetMediaEngineDefaults(&deps); - webrtc::FieldTrialBasedConfig trials; + webrtc::test::ScopedKeyValueConfig trials; deps.trials = &trials; std::unique_ptr engine = diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 58ae4995d7..e29d1ea66e 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -32,6 +32,7 @@ #include "media/engine/webrtc_media_engine.h" #include "media/engine/webrtc_voice_engine.h" #include "modules/rtp_rtcp/source/rtp_util.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/experiments/field_trial_units.h" @@ -82,13 +83,11 @@ const char* StreamTypeToString( return nullptr; } -bool IsEnabled(const webrtc::WebRtcKeyValueConfig& trials, - absl::string_view name) { +bool IsEnabled(const webrtc::FieldTrialsView& trials, absl::string_view name) { return absl::StartsWith(trials.Lookup(name), "Enabled"); } -bool IsDisabled(const webrtc::WebRtcKeyValueConfig& trials, - absl::string_view name) { +bool IsDisabled(const webrtc::FieldTrialsView& trials, absl::string_view name) { return absl::StartsWith(trials.Lookup(name), "Disabled"); } @@ -107,7 +106,7 @@ bool IsScaleFactorsPowerOfTwo(const webrtc::VideoEncoderConfig& config) { } void AddDefaultFeedbackParams(VideoCodec* codec, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { // Don't add any feedback params for RED and ULPFEC. if (codec->name == kRedCodecName || codec->name == kUlpfecCodecName) return; @@ -164,7 +163,7 @@ template std::vector GetPayloadTypesAndDefaultCodecs( const T* factory, bool is_decoder_factory, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { if (!factory) { return {}; } @@ -331,7 +330,7 @@ static bool ValidateStreamParams(const StreamParams& sp) { // Returns true if the given codec is disallowed from doing simulcast. bool IsCodecDisabledForSimulcast(const std::string& codec_name, - const webrtc::WebRtcKeyValueConfig& trials) { + const webrtc::FieldTrialsView& trials) { if (absl::EqualsIgnoreCase(codec_name, kVp9CodecName) || absl::EqualsIgnoreCase(codec_name, kAv1CodecName)) { return true; @@ -612,7 +611,7 @@ void DefaultUnsignalledSsrcHandler::SetDefaultSink( WebRtcVideoEngine::WebRtcVideoEngine( std::unique_ptr video_encoder_factory, std::unique_ptr video_decoder_factory, - const webrtc::WebRtcKeyValueConfig& trials) + const webrtc::FieldTrialsView& trials) : decoder_factory_(std::move(video_decoder_factory)), encoder_factory_(std::move(video_encoder_factory)), trials_(trials) { @@ -2478,7 +2477,8 @@ WebRtcVideoChannel::WebRtcVideoSendStream::CreateVideoEncoderConfig( encoder_config.simulcast_layers[i].active = rtp_parameters_.encodings[i].active; encoder_config.simulcast_layers[i].scalability_mode = - rtp_parameters_.encodings[i].scalability_mode; + webrtc::ScalabilityModeFromString( + rtp_parameters_.encodings[i].scalability_mode.value_or("")); if (rtp_parameters_.encodings[i].min_bitrate_bps) { encoder_config.simulcast_layers[i].min_bitrate_bps = *rtp_parameters_.encodings[i].min_bitrate_bps; @@ -3166,6 +3166,7 @@ WebRtcVideoChannel::WebRtcVideoReceiveStream::GetVideoReceiverInfo( info.frames_rendered = stats.frames_rendered; info.qp_sum = stats.qp_sum; info.total_decode_time_ms = stats.total_decode_time_ms; + info.total_processing_delay = stats.total_processing_delay; info.last_packet_received_timestamp_ms = stats.rtp_stats.last_packet_received_timestamp_ms; info.estimated_playout_ntp_timestamp_ms = @@ -3498,7 +3499,7 @@ EncoderStreamFactory::EncoderStreamFactory( int max_qp, bool is_screenshare, bool conference_mode, - const webrtc::WebRtcKeyValueConfig* trials) + const webrtc::FieldTrialsView* trials) : codec_name_(codec_name), max_qp_(max_qp), diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h index 940985d9f8..f70ebca334 100644 --- a/media/engine/webrtc_video_engine.h +++ b/media/engine/webrtc_video_engine.h @@ -94,7 +94,7 @@ class WebRtcVideoEngine : public VideoEngineInterface { WebRtcVideoEngine( std::unique_ptr video_encoder_factory, std::unique_ptr video_decoder_factory, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); ~WebRtcVideoEngine() override; @@ -116,7 +116,7 @@ class WebRtcVideoEngine : public VideoEngineInterface { const std::unique_ptr encoder_factory_; const std::unique_ptr bitrate_allocator_factory_; - const webrtc::WebRtcKeyValueConfig& trials_; + const webrtc::FieldTrialsView& trials_; }; class WebRtcVideoChannel : public VideoMediaChannel, @@ -658,7 +658,7 @@ class EncoderStreamFactory int max_qp, bool is_screenshare, bool conference_mode, - const webrtc::WebRtcKeyValueConfig* trials); + const webrtc::FieldTrialsView* trials); private: std::vector CreateEncoderStreams( @@ -686,7 +686,7 @@ class EncoderStreamFactory // layering and various settings. const bool conference_mode_; const webrtc::FieldTrialBasedConfig fallback_trials_; - const webrtc::WebRtcKeyValueConfig& trials_; + const webrtc::FieldTrialsView& trials_; }; } // namespace cricket diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc index a1aa88de7b..70fb43f8f1 100644 --- a/media/engine/webrtc_video_engine_unittest.cc +++ b/media/engine/webrtc_video_engine_unittest.cc @@ -60,11 +60,10 @@ #include "rtc_base/gunit.h" #include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/time_utils.h" -#include "system_wrappers/include/field_trial.h" #include "test/fake_decoder.h" -#include "test/field_trial.h" #include "test/frame_forwarder.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" using ::testing::_; @@ -175,7 +174,7 @@ rtc::scoped_refptr CreateBlackFrameBuffer( int height) { rtc::scoped_refptr buffer = webrtc::I420Buffer::Create(width, height); - webrtc::I420Buffer::SetBlack(buffer); + webrtc::I420Buffer::SetBlack(buffer.get()); return buffer; } @@ -244,12 +243,8 @@ class WebRtcVideoEngineTest : public ::testing::Test { public: WebRtcVideoEngineTest() : WebRtcVideoEngineTest("") {} explicit WebRtcVideoEngineTest(const std::string& field_trials) - : time_controller_(webrtc::Timestamp::Millis(4711)), - override_field_trials_( - field_trials.empty() - ? nullptr - : std::make_unique( - field_trials)), + : field_trials_(field_trials), + time_controller_(webrtc::Timestamp::Millis(4711)), task_queue_factory_(time_controller_.CreateTaskQueueFactory()), call_(webrtc::Call::Create([&] { webrtc::Call::Config call_config(&event_log_); @@ -286,9 +281,8 @@ class WebRtcVideoEngineTest : public ::testing::Test { void ExpectRtpCapabilitySupport(const char* uri, bool supported) const; + webrtc::test::ScopedKeyValueConfig field_trials_; webrtc::GlobalSimulatedTimeController time_controller_; - std::unique_ptr override_field_trials_; - webrtc::FieldTrialBasedConfig field_trials_; webrtc::RtcEventLogNull event_log_; std::unique_ptr task_queue_factory_; // Used in WebRtcVideoEngineVoiceTest, but defined here so it's properly @@ -936,9 +930,8 @@ TEST_F(WebRtcVideoEngineTest, } TEST_F(WebRtcVideoEngineTest, SimulcastEnabledForH264BehindFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-H264Simulcast/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-H264Simulcast/Enabled/"); AddSupportedVideoCodecType("H264"); std::unique_ptr channel(engine_.CreateMediaChannel( @@ -978,9 +971,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03SendCodecEnablesWithFieldTrial) { EXPECT_THAT(engine_.send_codecs(), Not(Contains(flexfec))); - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Enabled/"); EXPECT_THAT(engine_.send_codecs(), Contains(flexfec)); } @@ -993,9 +985,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03ReceiveCodecDisablesWithFieldTrial) { EXPECT_THAT(engine_.recv_codecs(), Contains(flexfec)); - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Disabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Disabled/"); EXPECT_THAT(engine_.recv_codecs(), Not(Contains(flexfec))); } @@ -1006,9 +997,8 @@ TEST_F(WebRtcVideoEngineTest, Flexfec03LowerPayloadTypeRange) { auto flexfec = Field("name", &VideoCodec::name, "flexfec-03"); // FlexFEC is active with field trial. - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-FlexFEC-03-Advertised/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FlexFEC-03-Advertised/Enabled/"); auto send_codecs = engine_.send_codecs(); auto it = std::find_if(send_codecs.begin(), send_codecs.end(), [](const cricket::VideoCodec& codec) { @@ -1452,7 +1442,7 @@ class WebRtcVideoChannelEncodedFrameCallbackTest : public ::testing::Test { } static const std::vector kSdpVideoFormats; - webrtc::FieldTrialBasedConfig field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; webrtc::RtcEventLogNull event_log_; std::unique_ptr task_queue_factory_; std::unique_ptr call_; @@ -1740,8 +1730,8 @@ class WebRtcVideoChannelBaseTest : public ::testing::Test { } webrtc::RtcEventLogNull event_log_; - webrtc::FieldTrialBasedConfig field_trials_; - std::unique_ptr override_field_trials_; + webrtc::test::ScopedKeyValueConfig field_trials_; + std::unique_ptr override_field_trials_; std::unique_ptr task_queue_factory_; std::unique_ptr call_; std::unique_ptr @@ -1796,9 +1786,8 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSize) { // Set field trial to override the default recv buffer size, and then re-run // setup where the interface is created and configured. const int kCustomRecvBufferSize = 123456; - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-IncreasedReceivebuffers/123456/"); + override_field_trials_ = std::make_unique( + field_trials_, "WebRTC-IncreasedReceivebuffers/123456/"); ResetTest(); @@ -1814,9 +1803,8 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSizeWithSuffix) { // Set field trial to override the default recv buffer size, and then re-run // setup where the interface is created and configured. const int kCustomRecvBufferSize = 123456; - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-IncreasedReceivebuffers/123456_Dogfood/"); + override_field_trials_ = std::make_unique( + field_trials_, "WebRTC-IncreasedReceivebuffers/123456_Dogfood/"); ResetTest(); EXPECT_TRUE(SetOneCodec(DefaultCodec())); @@ -1825,58 +1813,32 @@ TEST_F(WebRtcVideoChannelBaseTest, OverridesRecvBufferSizeWithSuffix) { EXPECT_EQ(kCustomRecvBufferSize, network_interface_.recvbuf_size()); } +class InvalidRecvBufferSizeFieldTrial + : public WebRtcVideoChannelBaseTest, + public ::testing::WithParamInterface {}; + // Test that we properly set the send and recv buffer sizes when overriding // via field trials that don't make any sense. -TEST_F(WebRtcVideoChannelBaseTest, InvalidRecvBufferSize) { +TEST_P(InvalidRecvBufferSizeFieldTrial, InvalidRecvBufferSize) { // Set bogus field trial values to override the default recv buffer size, and // then re-run setup where the interface is created and configured. The // default value should still be used. + override_field_trials_ = std::make_unique( + field_trials_, + std::string("WebRTC-IncreasedReceivebuffers/") + GetParam() + "/"); - const char* prev_field_trials = webrtc::field_trial::GetFieldTrialString(); - - std::string field_trial_string; - for (std::string group : {" ", "NotANumber", "-1", "0"}) { - std::string trial_string = "WebRTC-IncreasedReceivebuffers/"; - trial_string += group; - trial_string += "/"; - - // Dear reader. Sorry for this... it's a bit of a mess. - // TODO(bugs.webrtc.org/12854): This test needs to be rewritten to not use - // ResetTest and changing global field trials in a loop. - TearDown(); - // This is a hack to appease tsan. Because of the way the test is written - // active state within Call, including running task queues may race with - // the test changing the global field trial variable. - // This particular hack, pauses the transport controller TQ while we - // change the field trial. - rtc::TaskQueue* tq = call_->GetTransportControllerSend()->GetWorkerQueue(); - rtc::Event waiting, resume, conclude; - tq->PostTask([&waiting, &resume, &conclude]() { - waiting.Set(); - resume.Wait(rtc::Event::kForever); - conclude.Set(); - }); - - waiting.Wait(rtc::Event::kForever); - field_trial_string = std::move(trial_string); - webrtc::field_trial::InitFieldTrialsFromString(field_trial_string.c_str()); - - SetUp(); - resume.Set(); - // Ensure we don't cause a UAF as the test scope exits. - conclude.Wait(rtc::Event::kForever); - - // OK, now the test can carry on. - - EXPECT_TRUE(SetOneCodec(DefaultCodec())); - EXPECT_TRUE(SetSend(true)); - EXPECT_EQ(64 * 1024, network_interface_.sendbuf_size()); - EXPECT_EQ(256 * 1024, network_interface_.recvbuf_size()); - } + ResetTest(); - webrtc::field_trial::InitFieldTrialsFromString(prev_field_trials); + EXPECT_TRUE(SetOneCodec(DefaultCodec())); + EXPECT_TRUE(SetSend(true)); + EXPECT_EQ(64 * 1024, network_interface_.sendbuf_size()); + EXPECT_EQ(256 * 1024, network_interface_.recvbuf_size()); } +INSTANTIATE_TEST_SUITE_P(All, + InvalidRecvBufferSizeFieldTrial, + Values("NotANumber", "-1", " ", "0")); + // Test that stats work properly for a 1-1 call. TEST_F(WebRtcVideoChannelBaseTest, GetStats) { const int kDurationSec = 3; @@ -2497,7 +2459,7 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { AddSupportedVideoCodecType("H264"); #endif - fake_call_.reset(new FakeCall()); + fake_call_.reset(new FakeCall(&field_trials_)); channel_.reset(engine_.CreateMediaChannel( fake_call_.get(), GetMediaConfig(), VideoOptions(), webrtc::CryptoOptions(), video_bitrate_allocator_factory_.get())); @@ -2511,6 +2473,7 @@ class WebRtcVideoChannelTest : public WebRtcVideoEngineTest { void TearDown() override { channel_->SetInterface(nullptr); channel_ = nullptr; + fake_call_ = nullptr; } void ResetTest() { @@ -2949,8 +2912,8 @@ TEST_F(WebRtcVideoChannelTest, RecvAbsoluteSendTimeHeaderExtensions) { } TEST_F(WebRtcVideoChannelTest, FiltersExtensionsPicksTransportSeqNum) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FilterAbsSendTimeExtension/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-FilterAbsSendTimeExtension/Enabled/"); // Enable three redundant extensions. std::vector extensions; extensions.push_back(RtpExtension::kAbsSendTimeUri); @@ -3177,17 +3140,15 @@ TEST_F(WebRtcVideoChannelTest, LossNotificationIsDisabledByDefault) { } TEST_F(WebRtcVideoChannelTest, LossNotificationIsEnabledByFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-RtcpLossNotification/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-RtcpLossNotification/Enabled/"); ResetTest(); TestLossNotificationState(true); } TEST_F(WebRtcVideoChannelTest, LossNotificationCanBeEnabledAndDisabled) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-RtcpLossNotification/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-RtcpLossNotification/Enabled/"); ResetTest(); AssignDefaultCodec(); @@ -3788,8 +3749,8 @@ TEST_F(WebRtcVideoChannelTest, VerifyMinBitrate) { } TEST_F(WebRtcVideoChannelTest, VerifyMinBitrateWithForcedFallbackFieldTrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-VP8-Forced-Fallback-Encoder-v2/Enabled-1,2,34567/"); std::vector streams = AddSendStream()->GetVideoStreams(); ASSERT_EQ(1u, streams.size()); @@ -3798,9 +3759,8 @@ TEST_F(WebRtcVideoChannelTest, VerifyMinBitrateWithForcedFallbackFieldTrial) { TEST_F(WebRtcVideoChannelTest, BalancedDegradationPreferenceNotSupportedWithoutFieldtrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-Video-BalancedDegradation/Disabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Video-BalancedDegradation/Disabled/"); const bool kResolutionScalingEnabled = true; const bool kFpsScalingEnabled = false; TestDegradationPreference(kResolutionScalingEnabled, kFpsScalingEnabled); @@ -3808,9 +3768,8 @@ TEST_F(WebRtcVideoChannelTest, TEST_F(WebRtcVideoChannelTest, BalancedDegradationPreferenceSupportedBehindFieldtrial) { - RTC_DCHECK(!override_field_trials_); - override_field_trials_ = std::make_unique( - "WebRTC-Video-BalancedDegradation/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Video-BalancedDegradation/Enabled/"); const bool kResolutionScalingEnabled = true; const bool kFpsScalingEnabled = true; TestDegradationPreference(kResolutionScalingEnabled, kFpsScalingEnabled); @@ -4000,7 +3959,7 @@ TEST_F(WebRtcVideoChannelTest, SetDefaultSendCodecs) { VideoCodec codec; EXPECT_TRUE(channel_->GetSendCodec(&codec)); - EXPECT_TRUE(codec.Matches(engine_.send_codecs()[0])); + EXPECT_TRUE(codec.Matches(engine_.send_codecs()[0], &field_trials_)); // Using a RTX setup to verify that the default RTX payload type is good. const std::vector ssrcs = MAKE_VECTOR(kSsrcs1); @@ -5082,7 +5041,7 @@ TEST_F(WebRtcVideoChannelFlexfecRecvTest, SetRecvParamsWithoutFecDisablesFec) { ASSERT_EQ(1U, streams.size()); const FakeFlexfecReceiveStream* stream = streams.front(); EXPECT_EQ(GetEngineCodec("flexfec-03").id, stream->GetConfig().payload_type); - EXPECT_EQ(kFlexfecSsrc, stream->rtp_config().remote_ssrc); + EXPECT_EQ(kFlexfecSsrc, stream->remote_ssrc()); ASSERT_EQ(1U, stream->GetConfig().protected_media_ssrcs.size()); EXPECT_EQ(kSsrcs1[0], stream->GetConfig().protected_media_ssrcs[0]); @@ -7421,8 +7380,8 @@ TEST_F(WebRtcVideoChannelTest, // so that the bottom layer has width and height divisible by 2. // TODO(bugs.webrtc.org/8785): Remove this field trial when we fully trust // the number of simulcast layers set by the app. - webrtc::test::ScopedFieldTrials field_trial( - "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trial( + field_trials_, "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); // Set up WebRtcVideoChannel for 3-layer VP8 simulcast. VideoSendParameters parameters; @@ -7576,8 +7535,8 @@ TEST_F(WebRtcVideoChannelTest, // so that the bottom layer has width and height divisible by 2. // TODO(bugs.webrtc.org/8785): Remove this field trial when we fully trust // the number of simulcast layers set by the app. - webrtc::test::ScopedFieldTrials field_trial( - "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trial( + field_trials_, "WebRTC-NormalizeSimulcastResolution/Enabled-3/"); // Set up WebRtcVideoChannel for 3-layer H264 simulcast. encoder_factory_->AddSupportedVideoCodecType(kH264CodecName); @@ -8851,13 +8810,13 @@ class WebRtcVideoChannelSimulcastTest : public ::testing::Test { return streams[streams.size() - 1]; } + webrtc::test::ScopedKeyValueConfig field_trials_; webrtc::RtcEventLogNull event_log_; FakeCall fake_call_; cricket::FakeWebRtcVideoEncoderFactory* encoder_factory_; cricket::FakeWebRtcVideoDecoderFactory* decoder_factory_; std::unique_ptr mock_rate_allocator_factory_; - webrtc::FieldTrialBasedConfig field_trials_; WebRtcVideoEngine engine_; std::unique_ptr channel_; uint32_t last_ssrc_; diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc index 829cb82af1..82a30ffbf2 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -23,7 +23,7 @@ #include "api/audio/audio_frame_processor.h" #include "api/audio_codecs/audio_codec_pair_id.h" #include "api/call/audio_sink.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "media/base/audio_source.h" #include "media/base/media_constants.h" #include "media/base/stream_params.h" @@ -125,9 +125,10 @@ bool IsCodec(const AudioCodec& codec, const char* ref_name) { bool FindCodec(const std::vector& codecs, const AudioCodec& codec, - AudioCodec* found_codec) { + AudioCodec* found_codec, + const webrtc::FieldTrialsView* field_trials) { for (const AudioCodec& c : codecs) { - if (c.Matches(codec)) { + if (c.Matches(codec, field_trials)) { if (found_codec != NULL) { *found_codec = c; } @@ -205,16 +206,10 @@ absl::optional ComputeSendBitrate(int max_send_bitrate_bps, } } -bool IsEnabled(const webrtc::WebRtcKeyValueConfig& config, - absl::string_view trial) { +bool IsEnabled(const webrtc::FieldTrialsView& config, absl::string_view trial) { return absl::StartsWith(config.Lookup(trial), "Enabled"); } -bool IsDisabled(const webrtc::WebRtcKeyValueConfig& config, - absl::string_view trial) { - return absl::StartsWith(config.Lookup(trial), "Disabled"); -} - struct AdaptivePtimeConfig { bool enabled = false; webrtc::DataRate min_payload_bitrate = webrtc::DataRate::KilobitsPerSec(16); @@ -233,7 +228,7 @@ struct AdaptivePtimeConfig { "use_slow_adaptation", &use_slow_adaptation); } - explicit AdaptivePtimeConfig(const webrtc::WebRtcKeyValueConfig& trials) { + explicit AdaptivePtimeConfig(const webrtc::FieldTrialsView& trials) { Parser()->Parse(trials.Lookup("WebRTC-Audio-AdaptivePtime")); #if WEBRTC_ENABLE_PROTOBUF webrtc::audio_network_adaptor::config::ControllerManager config; @@ -303,7 +298,7 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( rtc::scoped_refptr audio_mixer, rtc::scoped_refptr audio_processing, webrtc::AudioFrameProcessor* audio_frame_processor, - const webrtc::WebRtcKeyValueConfig& trials) + const webrtc::FieldTrialsView& trials) : task_queue_factory_(task_queue_factory), adm_(adm), encoder_factory_(encoder_factory), @@ -311,8 +306,6 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( audio_mixer_(audio_mixer), apm_(audio_processing), audio_frame_processor_(audio_frame_processor), - audio_red_for_opus_enabled_( - !IsDisabled(trials, "WebRTC-Audio-Red-For-Opus")), minimized_remsampling_on_mobile_trial_enabled_( IsEnabled(trials, "WebRTC-Audio-MinimizeResamplingOnMobile")) { // This may be called from any thread, so detach thread checkers. @@ -399,10 +392,8 @@ void WebRtcVoiceEngine::Init() { #if defined(WEBRTC_IOS) // On iOS, VPIO provides built-in NS. options.noise_suppression = false; - options.typing_detection = false; #else options.noise_suppression = true; - options.typing_detection = true; #endif options.highpass_filter = true; options.stereo_swapping = false; @@ -459,11 +450,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { use_mobile_software_aec = true; #endif -// Override noise suppression options for Android. -#if defined(WEBRTC_ANDROID) - options.typing_detection = false; -#endif - // Set and adjust gain control options. #if defined(WEBRTC_IOS) // On iOS, VPIO provides built-in AGC. @@ -608,10 +594,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { RTC_LOG(LS_INFO) << "NS set to " << enabled; } - if (options.typing_detection) { - RTC_LOG(LS_WARNING) << "Typing detection is requested, but unsupported."; - } - ap->ApplyConfig(apm_config); return true; } @@ -738,7 +720,7 @@ std::vector WebRtcVoiceEngine::CollectCodecs( out.push_back(codec); - if (codec.name == kOpusCodecName && audio_red_for_opus_enabled_) { + if (codec.name == kOpusCodecName) { std::string redFmtp = rtc::ToString(codec.id) + "/" + rtc::ToString(codec.id); map_format({kRedCodecName, 48000, 2, {{"", redFmtp}}}, &out); @@ -1262,7 +1244,7 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream { RTC_LOG(LS_ERROR) << "Failed to SetBaseMinimumPlayoutDelayMs" " on AudioReceiveStream on SSRC=" - << stream_->rtp_config().remote_ssrc + << stream_->remote_ssrc() << " with delay_ms=" << delay_ms; return false; } @@ -1280,9 +1262,8 @@ class WebRtcVoiceMediaChannel::WebRtcAudioReceiveStream { webrtc::RtpParameters GetRtpParameters() const { webrtc::RtpParameters rtp_parameters; rtp_parameters.encodings.emplace_back(); - const auto& config = stream_->rtp_config(); - rtp_parameters.encodings[0].ssrc = config.remote_ssrc; - rtp_parameters.header_extensions = config.extensions; + rtp_parameters.encodings[0].ssrc = stream_->remote_ssrc(); + rtp_parameters.header_extensions = stream_->GetRtpExtensions(); return rtp_parameters; } @@ -1311,9 +1292,7 @@ WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel( engine_(engine), call_(call), audio_config_(config.audio), - crypto_options_(crypto_options), - audio_red_for_opus_enabled_( - !IsDisabled(call->trials(), "WebRTC-Audio-Red-For-Opus")) { + crypto_options_(crypto_options) { network_thread_checker_.Detach(); RTC_LOG(LS_VERBOSE) << "WebRtcVoiceMediaChannel::WebRtcVoiceMediaChannel"; RTC_DCHECK(call); @@ -1566,7 +1545,7 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( // Log a warning if a codec's payload type is changing. This used to be // treated as an error. It's abnormal, but not really illegal. AudioCodec old_codec; - if (FindCodec(recv_codecs_, codec, &old_codec) && + if (FindCodec(recv_codecs_, codec, &old_codec, &call_->trials()) && old_codec.id != codec.id) { RTC_LOG(LS_WARNING) << codec.name << " mapped to a second payload type (" << codec.id << ", was already mapped to " @@ -1574,7 +1553,7 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs( } auto format = AudioCodecToSdpAudioFormat(codec); if (!IsCodec(codec, kCnCodecName) && !IsCodec(codec, kDtmfCodecName) && - (!audio_red_for_opus_enabled_ || !IsCodec(codec, kRedCodecName)) && + !IsCodec(codec, kRedCodecName) && !engine()->decoder_factory_->IsSupportedDecoder(format)) { RTC_LOG(LS_ERROR) << "Unsupported codec: " << rtc::ToString(format); return false; @@ -1760,19 +1739,17 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( } } - if (audio_red_for_opus_enabled_) { - // Loop through the codecs to find the RED codec that matches opus - // with respect to clockrate and number of channels. - size_t red_codec_position = 0; - for (const AudioCodec& red_codec : codecs) { - if (red_codec_position < send_codec_position && - IsCodec(red_codec, kRedCodecName) && - CheckRedParameters(red_codec, *send_codec_spec)) { - send_codec_spec->red_payload_type = red_codec.id; - break; - } - red_codec_position++; + // Loop through the codecs to find the RED codec that matches opus + // with respect to clockrate and number of channels. + size_t red_codec_position = 0; + for (const AudioCodec& red_codec : codecs) { + if (red_codec_position < send_codec_position && + IsCodec(red_codec, kRedCodecName) && + CheckRedParameters(red_codec, *send_codec_spec)) { + send_codec_spec->red_payload_type = red_codec.id; + break; } + red_codec_position++; } if (send_codec_spec_ != send_codec_spec) { @@ -1796,7 +1773,6 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs( "streams."; recv_transport_cc_enabled_ = send_codec_spec_->transport_cc_enabled; recv_nack_enabled_ = send_codec_spec_->nack_enabled; - enable_non_sender_rtt_ = send_codec_spec_->enable_non_sender_rtt; for (auto& kv : recv_streams_) { kv.second->SetUseTransportCc(recv_transport_cc_enabled_, recv_nack_enabled_); @@ -2349,7 +2325,6 @@ bool WebRtcVoiceMediaChannel::GetStats(VoiceMediaInfo* info, sinfo.audio_level = stats.audio_level; sinfo.total_input_energy = stats.total_input_energy; sinfo.total_input_duration = stats.total_input_duration; - sinfo.typing_noise_detected = (send_ ? stats.typing_noise_detected : false); sinfo.ana_statistics = stats.ana_statistics; sinfo.apm_statistics = stats.apm_statistics; sinfo.report_block_datas = std::move(stats.report_block_datas); diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h index 1061d7a129..f15b9f5882 100644 --- a/media/engine/webrtc_voice_engine.h +++ b/media/engine/webrtc_voice_engine.h @@ -17,11 +17,11 @@ #include #include "api/audio_codecs/audio_encoder_factory.h" +#include "api/field_trials_view.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/task_queue/task_queue_factory.h" #include "api/transport/rtp/rtp_source.h" -#include "api/transport/webrtc_key_value_config.h" #include "call/audio_state.h" #include "call/call.h" #include "media/base/media_engine.h" @@ -55,7 +55,7 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { rtc::scoped_refptr audio_mixer, rtc::scoped_refptr audio_processing, webrtc::AudioFrameProcessor* audio_frame_processor, - const webrtc::WebRtcKeyValueConfig& trials); + const webrtc::FieldTrialsView& trials); WebRtcVoiceEngine() = delete; WebRtcVoiceEngine(const WebRtcVoiceEngine&) = delete; @@ -130,9 +130,6 @@ class WebRtcVoiceEngine final : public VoiceEngineInterface { int audio_jitter_buffer_min_delay_ms_ = 0; bool audio_jitter_buffer_enable_rtx_handling_ = false; - // If this field is enabled, we will negotiate and use RFC 2198 - // redundancy for opus audio. - const bool audio_red_for_opus_enabled_; const bool minimized_remsampling_on_mobile_trial_enabled_; }; @@ -325,8 +322,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel, // Unsignaled streams have an option to have a frame decryptor set on them. rtc::scoped_refptr unsignaled_frame_decryptor_; - - const bool audio_red_for_opus_enabled_; }; } // namespace cricket diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc index ad15a638bb..130fff3b3b 100644 --- a/media/engine/webrtc_voice_engine_unittest.cc +++ b/media/engine/webrtc_voice_engine_unittest.cc @@ -33,10 +33,10 @@ #include "rtc_base/arraysize.h" #include "rtc_base/byte_order.h" #include "rtc_base/numerics/safe_conversions.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_decoder_factory.h" #include "test/mock_audio_encoder_factory.h" +#include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::ContainerEq; @@ -145,7 +145,7 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) { webrtc::CreateDefaultTaskQueueFactory(); rtc::scoped_refptr adm = webrtc::test::MockAudioDeviceModule::CreateStrict(); - AdmSetupExpectations(adm); + AdmSetupExpectations(adm.get()); rtc::scoped_refptr> apm = use_null_apm ? nullptr : rtc::make_ref_counted< @@ -160,7 +160,7 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) { { webrtc::FieldTrialBasedConfig trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm, nullptr, trials); @@ -180,9 +180,7 @@ class FakeAudioSource : public cricket::AudioSource { class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { public: - WebRtcVoiceEngineTestFake() : WebRtcVoiceEngineTestFake("") {} - - explicit WebRtcVoiceEngineTestFake(const char* field_trials) + WebRtcVoiceEngineTestFake() : use_null_apm_(GetParam()), task_queue_factory_(webrtc::CreateDefaultTaskQueueFactory()), adm_(webrtc::test::MockAudioDeviceModule::CreateStrict()), @@ -190,10 +188,9 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { ? nullptr : rtc::make_ref_counted< StrictMock>()), - call_(), - override_field_trials_(field_trials) { + call_(&field_trials_) { // AudioDeviceModule. - AdmSetupExpectations(adm_); + AdmSetupExpectations(adm_.get()); if (!use_null_apm_) { // AudioProcessing. @@ -211,8 +208,8 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { auto encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); auto decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); engine_.reset(new cricket::WebRtcVoiceEngine( - task_queue_factory_.get(), adm_, encoder_factory, decoder_factory, - nullptr, apm_, nullptr, trials_config_)); + task_queue_factory_.get(), adm_.get(), encoder_factory, decoder_factory, + nullptr, apm_, nullptr, field_trials_)); engine_->Init(); send_parameters_.codecs.push_back(kPcmuCodec); recv_parameters_.codecs.push_back(kPcmuCodec); @@ -609,7 +606,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { stats.ana_statistics.frame_length_increase_counter = 765; stats.ana_statistics.frame_length_decrease_counter = 876; stats.ana_statistics.uplink_packet_loss_fraction = 987.0; - stats.typing_noise_detected = true; return stats; } void SetAudioSendStreamStats() { @@ -658,8 +654,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { stats.ana_statistics.frame_length_decrease_counter); EXPECT_EQ(info.ana_statistics.uplink_packet_loss_fraction, stats.ana_statistics.uplink_packet_loss_fraction); - EXPECT_EQ(info.typing_noise_detected, - stats.typing_noise_detected && is_sending); } webrtc::AudioReceiveStream::Stats GetAudioReceiveStreamStats() const { @@ -790,6 +784,7 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { protected: const bool use_null_apm_; + webrtc::test::ScopedKeyValueConfig field_trials_; std::unique_ptr task_queue_factory_; rtc::scoped_refptr adm_; rtc::scoped_refptr> apm_; @@ -800,10 +795,6 @@ class WebRtcVoiceEngineTestFake : public ::testing::TestWithParam { cricket::AudioRecvParameters recv_parameters_; FakeAudioSource fake_source_; webrtc::AudioProcessing::Config apm_config_; - - private: - webrtc::test::ScopedFieldTrials override_field_trials_; - webrtc::FieldTrialBasedConfig trials_config_; }; INSTANTIATE_TEST_SUITE_P(TestBothWithAndWithoutNullApm, @@ -1035,18 +1026,6 @@ TEST_P(WebRtcVoiceEngineTestFake, RecvRedDefault) { {112, {"red", 48000, 2, {{"", "111/111"}}}}}))); } -// Test that we disable Opus/Red with the kill switch. -TEST_P(WebRtcVoiceEngineTestFake, RecvRed) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Audio-Red-For-Opus/Disabled/"); - - EXPECT_TRUE(SetupRecvStream()); - cricket::AudioRecvParameters parameters; - parameters.codecs.push_back(kOpusCodec); - parameters.codecs.push_back(kRed48000Codec); - EXPECT_FALSE(channel_->SetRecvParameters(parameters)); -} - TEST_P(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) { EXPECT_TRUE(SetupSendStream()); @@ -1244,8 +1223,8 @@ TEST_P(WebRtcVoiceEngineTestFake, } TEST_P(WebRtcVoiceEngineTestFake, AdaptivePtimeFieldTrial) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Audio-AdaptivePtime/enabled:true/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Audio-AdaptivePtime/enabled:true/"); EXPECT_TRUE(SetupSendStream()); EXPECT_TRUE(GetAudioNetworkAdaptorConfig(kSsrcX)); } @@ -2515,14 +2494,6 @@ TEST_P(WebRtcVoiceEngineTestFake, AudioNetworkAdaptorNotGetOverridden) { GetAudioNetworkAdaptorConfig(kSsrcX)); } -class WebRtcVoiceEngineWithSendSideBweWithOverheadTest - : public WebRtcVoiceEngineTestFake { - public: - WebRtcVoiceEngineWithSendSideBweWithOverheadTest() - : WebRtcVoiceEngineTestFake( - "WebRTC-Audio-Allocation/min:6000bps,max:32000bps/") {} -}; - // Test that we can set the outgoing SSRC properly. // SSRC is set in SetupSendStream() by calling AddSendStream. TEST_P(WebRtcVoiceEngineTestFake, SetSendSsrc) { @@ -3640,7 +3611,7 @@ TEST(WebRtcVoiceEngineTest, StartupShutdown) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm, nullptr, field_trials); @@ -3670,7 +3641,7 @@ TEST(WebRtcVoiceEngineTest, StartupShutdownWithExternalADM) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm, nullptr, field_trials); @@ -3705,7 +3676,7 @@ TEST(WebRtcVoiceEngineTest, HasCorrectPayloadTypeMapping) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm, nullptr, field_trials); @@ -3758,7 +3729,7 @@ TEST(WebRtcVoiceEngineTest, Has32Channels) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::MockAudioDecoderFactory::CreateUnusedFactory(), nullptr, apm, nullptr, field_trials); @@ -3807,7 +3778,7 @@ TEST(WebRtcVoiceEngineTest, SetRecvCodecs) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, + task_queue_factory.get(), adm.get(), webrtc::MockAudioEncoderFactory::CreateUnusedFactory(), webrtc::CreateBuiltinAudioDecoderFactory(), nullptr, apm, nullptr, field_trials); @@ -3860,7 +3831,7 @@ TEST(WebRtcVoiceEngineTest, CollectRecvCodecs) { use_null_apm ? nullptr : webrtc::AudioProcessingBuilder().Create(); webrtc::FieldTrialBasedConfig field_trials; cricket::WebRtcVoiceEngine engine( - task_queue_factory.get(), adm, unused_encoder_factory, + task_queue_factory.get(), adm.get(), unused_encoder_factory, mock_decoder_factory, nullptr, apm, nullptr, field_trials); engine.Init(); auto codecs = engine.recv_codecs(); diff --git a/media/sctp/dcsctp_transport.cc b/media/sctp/dcsctp_transport.cc index 38d2799f36..3da6709702 100644 --- a/media/sctp/dcsctp_transport.cc +++ b/media/sctp/dcsctp_transport.cc @@ -10,6 +10,7 @@ #include "media/sctp/dcsctp_transport.h" +#include #include #include #include @@ -115,10 +116,21 @@ bool IsEmptyPPID(dcsctp::PPID ppid) { DcSctpTransport::DcSctpTransport(rtc::Thread* network_thread, rtc::PacketTransportInternal* transport, Clock* clock) + : DcSctpTransport(network_thread, + transport, + clock, + std::make_unique()) {} + +DcSctpTransport::DcSctpTransport( + rtc::Thread* network_thread, + rtc::PacketTransportInternal* transport, + Clock* clock, + std::unique_ptr socket_factory) : network_thread_(network_thread), transport_(transport), clock_(clock), random_(clock_->TimeInMicroseconds()), + socket_factory_(std::move(socket_factory)), task_queue_timeout_factory_( *network_thread, [this]() { return TimeMillis(); }, @@ -126,7 +138,7 @@ DcSctpTransport::DcSctpTransport(rtc::Thread* network_thread, socket_->HandleTimeout(timeout_id); }) { RTC_DCHECK_RUN_ON(network_thread_); - static int instance_count = 0; + static std::atomic instance_count = 0; rtc::StringBuilder sb; sb << debug_name_ << instance_count++; debug_name_ = sb.Release(); @@ -174,9 +186,8 @@ bool DcSctpTransport::Start(int local_sctp_port, std::make_unique(debug_name_); } - dcsctp::DcSctpSocketFactory factory; - socket_ = - factory.Create(debug_name_, *this, std::move(packet_observer), options); + socket_ = socket_factory_->Create(debug_name_, *this, + std::move(packet_observer), options); } else { if (local_sctp_port != socket_->options().local_port || remote_sctp_port != socket_->options().remote_port) { @@ -207,11 +218,21 @@ bool DcSctpTransport::OpenStream(int sid) { bool DcSctpTransport::ResetStream(int sid) { RTC_LOG(LS_INFO) << debug_name_ << "->ResetStream(" << sid << ")."; if (!socket_) { - RTC_LOG(LS_ERROR) << debug_name_ << "->OpenStream(sid=" << sid + RTC_LOG(LS_ERROR) << debug_name_ << "->ResetStream(sid=" << sid << "): Transport is not started."; return false; } + dcsctp::StreamID streams[1] = {dcsctp::StreamID(static_cast(sid))}; + + StreamClosingState& closing_state = closing_states_[streams[0]]; + if (closing_state.closure_initiated || closing_state.incoming_reset_done || + closing_state.outgoing_reset_done) { + // The closing procedure was already initiated by the remote, don't do + // anything. + return false; + } + closing_state.closure_initiated = true; socket_->ResetStreams(streams); return true; } @@ -471,7 +492,15 @@ void DcSctpTransport::OnStreamsResetPerformed( RTC_LOG(LS_INFO) << debug_name_ << "->OnStreamsResetPerformed(...): Outgoing stream reset" << ", sid=" << stream_id.value(); - SignalClosingProcedureComplete(stream_id.value()); + StreamClosingState& closing_state = closing_states_[stream_id]; + closing_state.outgoing_reset_done = true; + + if (closing_state.incoming_reset_done) { + // When the close was not initiated locally, we can signal the end of the + // data channel close procedure when the remote ACKs the reset. + SignalClosingProcedureComplete(stream_id.value()); + closing_states_.erase(stream_id); + } } } @@ -481,8 +510,24 @@ void DcSctpTransport::OnIncomingStreamsReset( RTC_LOG(LS_INFO) << debug_name_ << "->OnIncomingStreamsReset(...): Incoming stream reset" << ", sid=" << stream_id.value(); - SignalClosingProcedureStartedRemotely(stream_id.value()); - SignalClosingProcedureComplete(stream_id.value()); + StreamClosingState& closing_state = closing_states_[stream_id]; + closing_state.incoming_reset_done = true; + + if (!closing_state.closure_initiated) { + // When receiving an incoming stream reset event for a non local close + // procedure, the transport needs to reset the stream in the other + // direction too. + dcsctp::StreamID streams[1] = {stream_id}; + socket_->ResetStreams(streams); + SignalClosingProcedureStartedRemotely(stream_id.value()); + } + + if (closing_state.outgoing_reset_done) { + // The close procedure that was initiated locally is complete when we + // receive and incoming reset event. + SignalClosingProcedureComplete(stream_id.value()); + closing_states_.erase(stream_id); + } } } diff --git a/media/sctp/dcsctp_transport.h b/media/sctp/dcsctp_transport.h index 11c2f829c5..c62a28f3c5 100644 --- a/media/sctp/dcsctp_transport.h +++ b/media/sctp/dcsctp_transport.h @@ -21,9 +21,11 @@ #include "media/sctp/sctp_transport_internal.h" #include "net/dcsctp/public/dcsctp_options.h" #include "net/dcsctp/public/dcsctp_socket.h" +#include "net/dcsctp/public/dcsctp_socket_factory.h" #include "net/dcsctp/public/types.h" #include "net/dcsctp/timer/task_queue_timeout.h" #include "p2p/base/packet_transport_internal.h" +#include "rtc_base/containers/flat_map.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/random.h" #include "rtc_base/third_party/sigslot/sigslot.h" @@ -39,6 +41,10 @@ class DcSctpTransport : public cricket::SctpTransportInternal, DcSctpTransport(rtc::Thread* network_thread, rtc::PacketTransportInternal* transport, Clock* clock); + DcSctpTransport(rtc::Thread* network_thread, + rtc::PacketTransportInternal* transport, + Clock* clock, + std::unique_ptr socket_factory); ~DcSctpTransport() override; // cricket::SctpTransportInternal @@ -99,11 +105,28 @@ class DcSctpTransport : public cricket::SctpTransportInternal, Clock* clock_; Random random_; + std::unique_ptr socket_factory_; dcsctp::TaskQueueTimeoutFactory task_queue_timeout_factory_; std::unique_ptr socket_; std::string debug_name_ = "DcSctpTransport"; rtc::CopyOnWriteBuffer receive_buffer_; + // Used to keep track of the closing state of the data channel. + // Reset needs to happen both ways before signaling the transport + // is closed. + struct StreamClosingState { + // True when the local connection has initiated the reset. + // If a connection receives a reset for a stream that isn't + // already being reset locally, it needs to fire the signal + // SignalClosingProcedureStartedRemotely. + bool closure_initiated = false; + // True when the local connection received OnIncomingStreamsReset + bool incoming_reset_done = false; + // True when the local connection received OnStreamsResetPerformed + bool outgoing_reset_done = false; + }; + + flat_map closing_states_; bool ready_to_send_data_ = false; }; diff --git a/media/sctp/dcsctp_transport_unittest.cc b/media/sctp/dcsctp_transport_unittest.cc new file mode 100644 index 0000000000..119922c4df --- /dev/null +++ b/media/sctp/dcsctp_transport_unittest.cc @@ -0,0 +1,177 @@ +/* + * Copyright 2022 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 "media/sctp/dcsctp_transport.h" + +#include +#include + +#include "net/dcsctp/public/mock_dcsctp_socket.h" +#include "net/dcsctp/public/mock_dcsctp_socket_factory.h" +#include "p2p/base/fake_packet_transport.h" +#include "test/gtest.h" + +using ::testing::ByMove; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::NiceMock; +using ::testing::Return; + +namespace webrtc { + +namespace { +class SctpInternalTransportObserver : public sigslot::has_slots<> { + public: + MOCK_METHOD(void, OnSignalReadyToSendData, ()); + MOCK_METHOD(void, OnSignalAssociationChangeCommunicationUp, ()); + MOCK_METHOD(void, OnSignalClosingProcedureStartedRemotely, (int)); + MOCK_METHOD(void, OnSignalClosingProcedureComplete, (int)); +}; + +class Peer { + public: + Peer() : fake_packet_transport_("transport"), simulated_clock_(1000) { + auto socket_ptr = std::make_unique(); + socket_ = socket_ptr.get(); + + auto mock_dcsctp_socket_factory = + std::make_unique(); + EXPECT_CALL(*mock_dcsctp_socket_factory, Create) + .Times(1) + .WillOnce(Return(ByMove(std::move(socket_ptr)))); + + sctp_transport_ = std::make_unique( + rtc::Thread::Current(), &fake_packet_transport_, &simulated_clock_, + std::move(mock_dcsctp_socket_factory)); + + sctp_transport_->SignalAssociationChangeCommunicationUp.connect( + static_cast(&observer_), + &SctpInternalTransportObserver::OnSignalReadyToSendData); + sctp_transport_->SignalAssociationChangeCommunicationUp.connect( + static_cast(&observer_), + &SctpInternalTransportObserver:: + OnSignalAssociationChangeCommunicationUp); + sctp_transport_->SignalClosingProcedureStartedRemotely.connect( + static_cast(&observer_), + &SctpInternalTransportObserver:: + OnSignalClosingProcedureStartedRemotely); + sctp_transport_->SignalClosingProcedureComplete.connect( + static_cast(&observer_), + &SctpInternalTransportObserver::OnSignalClosingProcedureComplete); + } + + rtc::FakePacketTransport fake_packet_transport_; + webrtc::SimulatedClock simulated_clock_; + dcsctp::MockDcSctpSocket* socket_; + std::unique_ptr sctp_transport_; + NiceMock observer_; +}; +} // namespace + +TEST(DcSctpTransportTest, OpenSequence) { + Peer peer_a; + peer_a.fake_packet_transport_.SetWritable(true); + + EXPECT_CALL(*peer_a.socket_, Connect) + .Times(1) + .WillOnce(Invoke(peer_a.sctp_transport_.get(), + &dcsctp::DcSctpSocketCallbacks::OnConnected)); + EXPECT_CALL(peer_a.observer_, OnSignalReadyToSendData); + EXPECT_CALL(peer_a.observer_, OnSignalAssociationChangeCommunicationUp); + + peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); +} + +// Tests that the close sequence invoked from one end results in the stream to +// be reset from both ends and all the proper signals are sent. +TEST(DcSctpTransportTest, CloseSequence) { + Peer peer_a; + Peer peer_b; + peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_, + false); + { + InSequence sequence; + + EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) + .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); + + EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) + .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); + + EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureStartedRemotely(1)) + .Times(0); + EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureStartedRemotely(1)); + EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureComplete(1)); + EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureComplete(1)); + } + + peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); + peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024); + peer_a.sctp_transport_->OpenStream(1); + peer_a.sctp_transport_->ResetStream(1); + + // Simulate the callbacks from the stream resets + dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; + static_cast(peer_a.sctp_transport_.get()) + ->OnStreamsResetPerformed(streams); + static_cast(peer_b.sctp_transport_.get()) + ->OnIncomingStreamsReset(streams); + static_cast(peer_a.sctp_transport_.get()) + ->OnIncomingStreamsReset(streams); + static_cast(peer_b.sctp_transport_.get()) + ->OnStreamsResetPerformed(streams); +} + +// Tests that the close sequence initiated from both peers at the same time +// terminates properly. Both peers will think they initiated it, so no +// OnSignalClosingProcedureStartedRemotely should be called. +TEST(DcSctpTransportTest, CloseSequenceSimultaneous) { + Peer peer_a; + Peer peer_b; + peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_, + false); + { + InSequence sequence; + + EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) + .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); + + EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) + .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); + + EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureStartedRemotely(1)) + .Times(0); + EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureStartedRemotely(1)) + .Times(0); + EXPECT_CALL(peer_a.observer_, OnSignalClosingProcedureComplete(1)); + EXPECT_CALL(peer_b.observer_, OnSignalClosingProcedureComplete(1)); + } + + peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); + peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024); + peer_a.sctp_transport_->OpenStream(1); + peer_a.sctp_transport_->ResetStream(1); + peer_b.sctp_transport_->ResetStream(1); + + // Simulate the callbacks from the stream resets + dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; + static_cast(peer_a.sctp_transport_.get()) + ->OnStreamsResetPerformed(streams); + static_cast(peer_b.sctp_transport_.get()) + ->OnStreamsResetPerformed(streams); + static_cast(peer_a.sctp_transport_.get()) + ->OnIncomingStreamsReset(streams); + static_cast(peer_b.sctp_transport_.get()) + ->OnIncomingStreamsReset(streams); +} + +} // namespace webrtc diff --git a/media/sctp/sctp_transport_factory.cc b/media/sctp/sctp_transport_factory.cc index 071d7fdb23..457bc5f889 100644 --- a/media/sctp/sctp_transport_factory.cc +++ b/media/sctp/sctp_transport_factory.cc @@ -13,24 +13,15 @@ #include "rtc_base/system/unused.h" #ifdef WEBRTC_HAVE_DCSCTP -#include "media/sctp/dcsctp_transport.h" // nogncheck -#include "system_wrappers/include/clock.h" // nogncheck -#include "system_wrappers/include/field_trial.h" // nogncheck -#endif - -#ifdef WEBRTC_HAVE_USRSCTP -#include "media/sctp/usrsctp_transport.h" // nogncheck +#include "media/sctp/dcsctp_transport.h" // nogncheck +#include "system_wrappers/include/clock.h" // nogncheck #endif namespace cricket { SctpTransportFactory::SctpTransportFactory(rtc::Thread* network_thread) - : network_thread_(network_thread), use_usrsctp_("Disabled", false) { + : network_thread_(network_thread) { RTC_UNUSED(network_thread_); -#ifdef WEBRTC_HAVE_DCSCTP - webrtc::ParseFieldTrial({&use_usrsctp_}, webrtc::field_trial::FindFullName( - "WebRTC-DataChannel-Dcsctp")); -#endif } std::unique_ptr @@ -38,16 +29,8 @@ SctpTransportFactory::CreateSctpTransport( rtc::PacketTransportInternal* transport) { std::unique_ptr result; #ifdef WEBRTC_HAVE_DCSCTP - if (!use_usrsctp_.Get()) { - result = std::unique_ptr(new webrtc::DcSctpTransport( - network_thread_, transport, webrtc::Clock::GetRealTimeClock())); - } -#endif -#ifdef WEBRTC_HAVE_USRSCTP - if (!result) { - result = std::unique_ptr( - new UsrsctpTransport(network_thread_, transport)); - } + result = std::unique_ptr(new webrtc::DcSctpTransport( + network_thread_, transport, webrtc::Clock::GetRealTimeClock())); #endif return result; } diff --git a/media/sctp/sctp_transport_factory.h b/media/sctp/sctp_transport_factory.h index 9ae246a6a6..4fff214129 100644 --- a/media/sctp/sctp_transport_factory.h +++ b/media/sctp/sctp_transport_factory.h @@ -15,7 +15,6 @@ #include "api/transport/sctp_transport_factory_interface.h" #include "media/sctp/sctp_transport_internal.h" -#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/thread.h" namespace cricket { @@ -29,7 +28,6 @@ class SctpTransportFactory : public webrtc::SctpTransportFactoryInterface { private: rtc::Thread* network_thread_; - webrtc::FieldTrialFlag use_usrsctp_; }; } // namespace cricket diff --git a/media/sctp/usrsctp_transport.cc b/media/sctp/usrsctp_transport.cc deleted file mode 100644 index 4babf110a2..0000000000 --- a/media/sctp/usrsctp_transport.cc +++ /dev/null @@ -1,1575 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include -namespace { -// Some ERRNO values get re-#defined to WSA* equivalents in some talk/ -// headers. We save the original ones in an enum. -enum PreservedErrno { - SCTP_EINPROGRESS = EINPROGRESS, - SCTP_EWOULDBLOCK = EWOULDBLOCK -}; - -// Successful return value from usrsctp callbacks. Is not actually used by -// usrsctp, but all example programs for usrsctp use 1 as their return value. -constexpr int kSctpSuccessReturn = 1; -constexpr int kSctpErrorReturn = 0; - -} // namespace - -#include -#include -#include - -#include -#include -#include - -#include "absl/algorithm/container.h" -#include "absl/base/attributes.h" -#include "absl/types/optional.h" -#include "api/sequence_checker.h" -#include "media/base/codec.h" -#include "media/base/media_channel.h" -#include "media/base/media_constants.h" -#include "media/base/stream_params.h" -#include "media/sctp/usrsctp_transport.h" -#include "p2p/base/dtls_transport_internal.h" // For PF_NORMAL -#include "rtc_base/arraysize.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/helpers.h" -#include "rtc_base/logging.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/string_utils.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/task_utils/to_queued_task.h" -#include "rtc_base/thread_annotations.h" -#include "rtc_base/trace_event.h" - -namespace cricket { -namespace { - -// The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280, -// take off 85 bytes for DTLS/TURN/TCP/IP and ciphertext overhead. -// -// Additionally, it's possible that TURN adds an additional 4 bytes of overhead -// after a channel has been established, so we subtract an additional 4 bytes. -// -// 1280 IPV6 MTU -// -40 IPV6 header -// -8 UDP -// -24 GCM Cipher -// -13 DTLS record header -// -4 TURN ChannelData -// = 1191 bytes. -static constexpr size_t kSctpMtu = 1191; - -// Set the initial value of the static SCTP Data Engines reference count. -ABSL_CONST_INIT int g_usrsctp_usage_count = 0; -ABSL_CONST_INIT bool g_usrsctp_initialized_ = false; -ABSL_CONST_INIT webrtc::GlobalMutex g_usrsctp_lock_(absl::kConstInit); -ABSL_CONST_INIT char kZero[] = {'\0'}; - -// DataMessageType is used for the SCTP "Payload Protocol Identifier", as -// defined in http://tools.ietf.org/html/rfc4960#section-14.4 -// -// For the list of IANA approved values see: -// https://tools.ietf.org/html/rfc8831 Sec. 8 -// http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xml -// The value is not used by SCTP itself. It indicates the protocol running -// on top of SCTP. -enum { - PPID_NONE = 0, // No protocol is specified. - PPID_CONTROL = 50, - PPID_TEXT_LAST = 51, - PPID_BINARY_PARTIAL = 52, // Deprecated - PPID_BINARY_LAST = 53, - PPID_TEXT_PARTIAL = 54, // Deprecated - PPID_TEXT_EMPTY = 56, - PPID_BINARY_EMPTY = 57, -}; - -// Should only be modified by UsrSctpWrapper. -ABSL_CONST_INIT cricket::UsrsctpTransportMap* g_transport_map_ = nullptr; - -// Helper that will call C's free automatically. -// TODO(b/181900299): Figure out why unique_ptr with a custom deleter is causing -// issues in a certain build environment. -class AutoFreedPointer { - public: - explicit AutoFreedPointer(void* ptr) : ptr_(ptr) {} - AutoFreedPointer(AutoFreedPointer&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; } - ~AutoFreedPointer() { free(ptr_); } - - void* get() const { return ptr_; } - - private: - void* ptr_; -}; - -// Helper for logging SCTP messages. -#if defined(__GNUC__) -__attribute__((__format__(__printf__, 1, 2))) -#endif -void DebugSctpPrintf(const char* format, ...) { -#if RTC_DCHECK_IS_ON - char s[255]; - va_list ap; - va_start(ap, format); - vsnprintf(s, sizeof(s), format, ap); - RTC_LOG(LS_INFO) << "SCTP: " << s; - va_end(ap); -#endif -} - -// Get the PPID to use for the terminating fragment of this type. -uint32_t GetPpid(webrtc::DataMessageType type, size_t size) { - switch (type) { - case webrtc::DataMessageType::kControl: - return PPID_CONTROL; - case webrtc::DataMessageType::kBinary: - return size > 0 ? PPID_BINARY_LAST : PPID_BINARY_EMPTY; - case webrtc::DataMessageType::kText: - return size > 0 ? PPID_TEXT_LAST : PPID_TEXT_EMPTY; - } -} - -bool GetDataMediaType(uint32_t ppid, webrtc::DataMessageType* dest) { - RTC_DCHECK(dest != NULL); - switch (ppid) { - case PPID_BINARY_PARTIAL: - case PPID_BINARY_LAST: - case PPID_BINARY_EMPTY: - *dest = webrtc::DataMessageType::kBinary; - return true; - - case PPID_TEXT_PARTIAL: - case PPID_TEXT_LAST: - case PPID_TEXT_EMPTY: - *dest = webrtc::DataMessageType::kText; - return true; - - case PPID_CONTROL: - *dest = webrtc::DataMessageType::kControl; - return true; - } - return false; -} - -bool IsEmptyPPID(uint32_t ppid) { - return ppid == PPID_BINARY_EMPTY || ppid == PPID_TEXT_EMPTY; -} - -// Log the packet in text2pcap format, if log level is at LS_VERBOSE. -// -// In order to turn these logs into a pcap file you can use, first filter the -// "SCTP_PACKET" log lines: -// -// cat chrome_debug.log | grep SCTP_PACKET > filtered.log -// -// Then run through text2pcap: -// -// text2pcap -n -l 248 -D -t '%H:%M:%S.' filtered.log filtered.pcapng -// -// Command flag information: -// -n: Outputs to a pcapng file, can specify inbound/outbound packets. -// -l: Specifies the link layer header type. 248 means SCTP. See: -// http://www.tcpdump.org/linktypes.html -// -D: Text before packet specifies if it is inbound or outbound. -// -t: Time format. -// -// Why do all this? Because SCTP goes over DTLS, which is encrypted. So just -// getting a normal packet capture won't help you, unless you have the DTLS -// keying material. -void VerboseLogPacket(const void* data, size_t length, int direction) { - if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) { - char* dump_buf; - // Some downstream project uses an older version of usrsctp that expects - // a non-const "void*" as first parameter when dumping the packet, so we - // need to cast the const away here to avoid a compiler error. - if ((dump_buf = usrsctp_dumppacket(const_cast(data), length, - direction)) != NULL) { - RTC_LOG(LS_VERBOSE) << dump_buf; - usrsctp_freedumpbuffer(dump_buf); - } - } -} - -// Creates the sctp_sendv_spa struct used for setting flags in the -// sctp_sendv() call. -sctp_sendv_spa CreateSctpSendParams(int sid, - const webrtc::SendDataParams& params, - size_t size) { - struct sctp_sendv_spa spa = {0}; - spa.sendv_flags |= SCTP_SEND_SNDINFO_VALID; - spa.sendv_sndinfo.snd_sid = sid; - spa.sendv_sndinfo.snd_ppid = rtc::HostToNetwork32(GetPpid(params.type, size)); - // Explicitly marking the EOR flag turns the usrsctp_sendv call below into a - // non atomic operation. This means that the sctp lib might only accept the - // message partially. This is done in order to improve throughput, so that we - // don't have to wait for an empty buffer to send the max message length, for - // example. - spa.sendv_sndinfo.snd_flags |= SCTP_EOR; - - if (!params.ordered) { - spa.sendv_sndinfo.snd_flags |= SCTP_UNORDERED; - } - if (params.max_rtx_count.has_value()) { - RTC_DCHECK(*params.max_rtx_count >= 0 && - *params.max_rtx_count <= std::numeric_limits::max()); - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_RTX; - spa.sendv_prinfo.pr_value = *params.max_rtx_count; - } - if (params.max_rtx_ms.has_value()) { - RTC_DCHECK(*params.max_rtx_ms >= 0 && - *params.max_rtx_ms <= std::numeric_limits::max()); - spa.sendv_flags |= SCTP_SEND_PRINFO_VALID; - spa.sendv_prinfo.pr_policy = SCTP_PR_SCTP_TTL; - spa.sendv_prinfo.pr_value = *params.max_rtx_ms; - } - return spa; -} - -std::string SctpErrorCauseCodeToString(SctpErrorCauseCode code) { - switch (code) { - case SctpErrorCauseCode::kInvalidStreamIdentifier: - return "Invalid Stream Identifier"; - case SctpErrorCauseCode::kMissingMandatoryParameter: - return "Missing Mandatory Parameter"; - case SctpErrorCauseCode::kStaleCookieError: - return "Stale Cookie Error"; - case SctpErrorCauseCode::kOutOfResource: - return "Out of Resource"; - case SctpErrorCauseCode::kUnresolvableAddress: - return "Unresolvable Address"; - case SctpErrorCauseCode::kUnrecognizedChunkType: - return "Unrecognized Chunk Type"; - case SctpErrorCauseCode::kInvalidMandatoryParameter: - return "Invalid Mandatory Parameter"; - case SctpErrorCauseCode::kUnrecognizedParameters: - return "Unrecognized Parameters"; - case SctpErrorCauseCode::kNoUserData: - return "No User Data"; - case SctpErrorCauseCode::kCookieReceivedWhileShuttingDown: - return "Cookie Received Whilte Shutting Down"; - case SctpErrorCauseCode::kRestartWithNewAddresses: - return "Restart With New Addresses"; - case SctpErrorCauseCode::kUserInitiatedAbort: - return "User Initiated Abort"; - case SctpErrorCauseCode::kProtocolViolation: - return "Protocol Violation"; - } - return "Unknown error"; -} -} // namespace - -// Maps SCTP transport ID to UsrsctpTransport object, necessary in send -// threshold callback and outgoing packet callback. It also provides a facility -// to safely post a task to an UsrsctpTransport's network thread from another -// thread. -class UsrsctpTransportMap { - public: - UsrsctpTransportMap() = default; - - // Assigns a new unused ID to the following transport. - uintptr_t Register(cricket::UsrsctpTransport* transport) { - webrtc::MutexLock lock(&lock_); - // usrsctp_connect fails with a value of 0... - if (next_id_ == 0) { - ++next_id_; - } - // In case we've wrapped around and need to find an empty spot from a - // removed transport. Assumes we'll never be full. - while (map_.find(next_id_) != map_.end()) { - ++next_id_; - if (next_id_ == 0) { - ++next_id_; - } - } - map_[next_id_] = transport; - return next_id_++; - } - - // Returns true if found. - bool Deregister(uintptr_t id) { - webrtc::MutexLock lock(&lock_); - return map_.erase(id) > 0; - } - - // Posts `action` to the network thread of the transport identified by `id` - // and returns true if found, all while holding a lock to protect against the - // transport being simultaneously deleted/deregistered, or returns false if - // not found. - template - bool PostToTransportThread(uintptr_t id, F action) const { - webrtc::MutexLock lock(&lock_); - UsrsctpTransport* transport = RetrieveWhileHoldingLock(id); - if (!transport) { - return false; - } - transport->network_thread_->PostTask(ToQueuedTask( - transport->task_safety_, - [transport, action{std::move(action)}]() { action(transport); })); - return true; - } - - private: - UsrsctpTransport* RetrieveWhileHoldingLock(uintptr_t id) const - RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_) { - auto it = map_.find(id); - if (it == map_.end()) { - return nullptr; - } - return it->second; - } - - mutable webrtc::Mutex lock_; - - uintptr_t next_id_ RTC_GUARDED_BY(lock_) = 0; - std::unordered_map map_ RTC_GUARDED_BY(lock_); -}; - -// Handles global init/deinit, and mapping from usrsctp callbacks to -// UsrsctpTransport calls. -class UsrsctpTransport::UsrSctpWrapper { - public: - static void InitializeUsrSctp() { - RTC_LOG(LS_INFO) << __FUNCTION__; - // UninitializeUsrSctp tries to call usrsctp_finish in a loop for three - // seconds; if that failed and we were left in a still-initialized state, we - // don't want to call usrsctp_init again as that will result in undefined - // behavior. - if (g_usrsctp_initialized_) { - RTC_LOG(LS_WARNING) << "Not reinitializing usrsctp since last attempt at " - "usrsctp_finish failed."; - } else { - // First argument is udp_encapsulation_port, which is not releveant for - // our AF_CONN use of sctp. - usrsctp_init(0, &UsrSctpWrapper::OnSctpOutboundPacket, &DebugSctpPrintf); - g_usrsctp_initialized_ = true; - } - - // To turn on/off detailed SCTP debugging. You will also need to have the - // SCTP_DEBUG cpp defines flag, which can be turned on in media/BUILD.gn. - // usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL); - - // TODO(ldixon): Consider turning this on/off. - usrsctp_sysctl_set_sctp_ecn_enable(0); - - // WebRTC doesn't use these features, so disable them to reduce the - // potential attack surface. - usrsctp_sysctl_set_sctp_asconf_enable(0); - usrsctp_sysctl_set_sctp_auth_enable(0); - - // This is harmless, but we should find out when the library default - // changes. - int send_size = usrsctp_sysctl_get_sctp_sendspace(); - if (send_size != kSctpSendBufferSize) { - RTC_LOG(LS_ERROR) << "Got different send size than expected: " - << send_size; - } - - // TODO(ldixon): Consider turning this on/off. - // This is not needed right now (we don't do dynamic address changes): - // If SCTP Auto-ASCONF is enabled, the peer is informed automatically - // when a new address is added or removed. This feature is enabled by - // default. - // usrsctp_sysctl_set_sctp_auto_asconf(0); - - // TODO(ldixon): Consider turning this on/off. - // Add a blackhole sysctl. Setting it to 1 results in no ABORTs - // being sent in response to INITs, setting it to 2 results - // in no ABORTs being sent for received OOTB packets. - // This is similar to the TCP sysctl. - // - // See: http://lakerest.net/pipermail/sctp-coders/2012-January/009438.html - // See: http://svnweb.freebsd.org/base?view=revision&revision=229805 - // usrsctp_sysctl_set_sctp_blackhole(2); - - // Set the number of default outgoing streams. This is the number we'll - // send in the SCTP INIT message. - usrsctp_sysctl_set_sctp_nr_outgoing_streams_default(kMaxSctpStreams); - - g_transport_map_ = new UsrsctpTransportMap(); - } - - static void UninitializeUsrSctp() { - RTC_LOG(LS_INFO) << __FUNCTION__; - // usrsctp_finish() may fail if it's called too soon after the transports - // are - // closed. Wait and try again until it succeeds for up to 3 seconds. - for (size_t i = 0; i < 300; ++i) { - if (usrsctp_finish() == 0) { - g_usrsctp_initialized_ = false; - delete g_transport_map_; - g_transport_map_ = nullptr; - return; - } - - rtc::Thread::SleepMs(10); - } - delete g_transport_map_; - g_transport_map_ = nullptr; - RTC_LOG(LS_ERROR) << "Failed to shutdown usrsctp."; - } - - static void IncrementUsrSctpUsageCount() { - webrtc::GlobalMutexLock lock(&g_usrsctp_lock_); - if (!g_usrsctp_usage_count) { - InitializeUsrSctp(); - } - ++g_usrsctp_usage_count; - } - - static void DecrementUsrSctpUsageCount() { - webrtc::GlobalMutexLock lock(&g_usrsctp_lock_); - --g_usrsctp_usage_count; - if (!g_usrsctp_usage_count) { - UninitializeUsrSctp(); - } - } - - // This is the callback usrsctp uses when there's data to send on the network - // that has been wrapped appropriatly for the SCTP protocol. - static int OnSctpOutboundPacket(void* addr, - void* data, - size_t length, - uint8_t tos, - uint8_t set_df) { - if (!g_transport_map_) { - RTC_LOG(LS_ERROR) - << "OnSctpOutboundPacket called after usrsctp uninitialized?"; - return EINVAL; - } - RTC_LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" - "addr: " - << addr << "; length: " << length - << "; tos: " << rtc::ToHex(tos) - << "; set_df: " << rtc::ToHex(set_df); - - VerboseLogPacket(data, length, SCTP_DUMP_OUTBOUND); - - // Note: We have to copy the data; the caller will delete it. - rtc::CopyOnWriteBuffer buf(reinterpret_cast(data), length); - - // PostsToTransportThread protects against the transport being - // simultaneously deregistered/deleted, since this callback may come from - // the SCTP timer thread and thus race with the network thread. - bool found = g_transport_map_->PostToTransportThread( - reinterpret_cast(addr), [buf](UsrsctpTransport* transport) { - transport->OnPacketFromSctpToNetwork(buf); - }); - if (!found) { - RTC_LOG(LS_ERROR) - << "OnSctpOutboundPacket: Failed to get transport for socket ID " - << addr << "; possibly was already destroyed."; - return EINVAL; - } - - return 0; - } - - // This is the callback called from usrsctp when data has been received, after - // a packet has been interpreted and parsed by usrsctp and found to contain - // payload data. It is called by a usrsctp thread. It is assumed this function - // will free the memory used by 'data'. - static int OnSctpInboundPacket(struct socket* sock, - union sctp_sockstore addr, - void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags, - void* ulp_info) { - AutoFreedPointer owned_data(data); - - if (!g_transport_map_) { - RTC_LOG(LS_ERROR) - << "OnSctpInboundPacket called after usrsctp uninitialized?"; - return kSctpErrorReturn; - } - - uintptr_t id = reinterpret_cast(ulp_info); - - // PostsToTransportThread protects against the transport being - // simultaneously deregistered/deleted, since this callback may come from - // the SCTP timer thread and thus race with the network thread. - bool found = g_transport_map_->PostToTransportThread( - id, [owned_data{std::move(owned_data)}, length, rcv, - flags](UsrsctpTransport* transport) { - transport->OnDataOrNotificationFromSctp(owned_data.get(), length, rcv, - flags); - }); - if (!found) { - RTC_LOG(LS_ERROR) - << "OnSctpInboundPacket: Failed to get transport for socket ID " << id - << "; possibly was already destroyed."; - return kSctpErrorReturn; - } - return kSctpSuccessReturn; - } - - static int SendThresholdCallback(struct socket* sock, - uint32_t sb_free, - void* ulp_info) { - // Fired on our I/O thread. UsrsctpTransport::OnPacketReceived() gets - // a packet containing acknowledgments, which goes into usrsctp_conninput, - // and then back here. - if (!g_transport_map_) { - RTC_LOG(LS_ERROR) - << "SendThresholdCallback called after usrsctp uninitialized?"; - return 0; - } - - uintptr_t id = reinterpret_cast(ulp_info); - - bool found = g_transport_map_->PostToTransportThread( - id, [](UsrsctpTransport* transport) { - transport->OnSendThresholdCallback(); - }); - if (!found) { - RTC_LOG(LS_ERROR) - << "SendThresholdCallback: Failed to get transport for socket ID " - << id << "; possibly was already destroyed."; - } - return 0; - } -}; - -UsrsctpTransport::UsrsctpTransport(rtc::Thread* network_thread, - rtc::PacketTransportInternal* transport) - : network_thread_(network_thread), - transport_(transport), - was_ever_writable_(transport ? transport->writable() : false) { - RTC_DCHECK(network_thread_); - RTC_DCHECK_RUN_ON(network_thread_); - ConnectTransportSignals(); -} - -UsrsctpTransport::~UsrsctpTransport() { - RTC_DCHECK_RUN_ON(network_thread_); - // Close abruptly; no reset procedure. - CloseSctpSocket(); - // It's not strictly necessary to reset these fields to nullptr, - // but having these fields set to nullptr is a clear indication that - // object was destructed. There was a bug in usrsctp when it - // invoked OnSctpOutboundPacket callback for destructed UsrsctpTransport, - // which caused obscure SIGSEGV on access to these fields, - // having this fields set to nullptr will make it easier to understand - // that UsrsctpTransport was destructed and "use-after-free" bug happen. - // SIGSEGV error triggered on dereference these pointers will also - // be easier to understand due to 0x0 address. All of this assumes - // that ASAN is not enabled to detect "use-after-free", which is - // currently default configuration. - network_thread_ = nullptr; - transport_ = nullptr; -} - -void UsrsctpTransport::SetDtlsTransport( - rtc::PacketTransportInternal* transport) { - RTC_DCHECK_RUN_ON(network_thread_); - DisconnectTransportSignals(); - transport_ = transport; - ConnectTransportSignals(); - if (!was_ever_writable_ && transport && transport->writable()) { - was_ever_writable_ = true; - // New transport is writable, now we can start the SCTP connection if Start - // was called already. - if (started_) { - RTC_DCHECK(!sock_); - Connect(); - } - } -} - -bool UsrsctpTransport::Start(int local_sctp_port, - int remote_sctp_port, - int max_message_size) { - RTC_DCHECK_RUN_ON(network_thread_); - if (local_sctp_port == -1) { - local_sctp_port = kSctpDefaultPort; - } - if (remote_sctp_port == -1) { - remote_sctp_port = kSctpDefaultPort; - } - if (max_message_size > kSctpSendBufferSize) { - RTC_LOG(LS_ERROR) << "Max message size of " << max_message_size - << " is larger than send bufffer size " - << kSctpSendBufferSize; - return false; - } - if (max_message_size < 1) { - RTC_LOG(LS_ERROR) << "Max message size of " << max_message_size - << " is too small"; - return false; - } - // We allow changing max_message_size with a second Start() call, - // but not changing the port numbers. - max_message_size_ = max_message_size; - if (started_) { - if (local_sctp_port != local_port_ || remote_sctp_port != remote_port_) { - RTC_LOG(LS_ERROR) - << "Can't change SCTP port after SCTP association formed."; - return false; - } - return true; - } - local_port_ = local_sctp_port; - remote_port_ = remote_sctp_port; - started_ = true; - RTC_DCHECK(!sock_); - // Only try to connect if the DTLS transport has been writable before - // (indicating that the DTLS handshake is complete). - if (was_ever_writable_) { - return Connect(); - } - return true; -} - -bool UsrsctpTransport::OpenStream(int sid) { - RTC_DCHECK_RUN_ON(network_thread_); - if (sid > kMaxSctpSid) { - RTC_LOG(LS_WARNING) << debug_name_ - << "->OpenStream(...): " - "Not adding data stream " - "with sid=" - << sid << " because sid is too high."; - return false; - } - auto it = stream_status_by_sid_.find(sid); - if (it == stream_status_by_sid_.end()) { - stream_status_by_sid_[sid] = StreamStatus(); - return true; - } - if (it->second.is_open()) { - RTC_LOG(LS_WARNING) << debug_name_ - << "->OpenStream(...): " - "Not adding data stream " - "with sid=" - << sid << " because stream is already open."; - return false; - } else { - RTC_LOG(LS_WARNING) << debug_name_ - << "->OpenStream(...): " - "Not adding data stream " - " with sid=" - << sid << " because stream is still closing."; - return false; - } -} - -bool UsrsctpTransport::ResetStream(int sid) { - RTC_DCHECK_RUN_ON(network_thread_); - - auto it = stream_status_by_sid_.find(sid); - if (it == stream_status_by_sid_.end() || !it->second.is_open()) { - RTC_LOG(LS_WARNING) << debug_name_ << "->ResetStream(" << sid - << "): stream not open."; - return false; - } - - RTC_LOG(LS_VERBOSE) << debug_name_ << "->ResetStream(" << sid - << "): " - "Queuing RE-CONFIG chunk."; - it->second.closure_initiated = true; - - // Signal our stream-reset logic that it should try to send now, if it can. - SendQueuedStreamResets(); - - // The stream will actually get removed when we get the acknowledgment. - return true; -} - -bool UsrsctpTransport::SendData(int sid, - const webrtc::SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result) { - RTC_DCHECK_RUN_ON(network_thread_); - - if (partial_outgoing_message_.has_value()) { - if (result) { - *result = SDR_BLOCK; - } - // Ready to send should get set only when SendData() call gets blocked. - ready_to_send_data_ = false; - return false; - } - - // Do not queue data to send on a closing stream. - auto it = stream_status_by_sid_.find(sid); - if (it == stream_status_by_sid_.end() || !it->second.is_open()) { - RTC_LOG(LS_WARNING) - << debug_name_ - << "->SendData(...): " - "Not sending data because sid is unknown or closing: " - << sid; - if (result) { - *result = SDR_ERROR; - } - return false; - } - - size_t payload_size = payload.size(); - OutgoingMessage message(payload, sid, params); - SendDataResult send_message_result = SendMessageInternal(&message); - if (result) { - *result = send_message_result; - } - if (payload_size == message.size()) { - // Nothing was sent. - return false; - } - // If any data is sent, we accept the message. In the case that data was - // partially accepted by the sctp library, the remaining is buffered. This - // ensures the client does not resend the message. - RTC_DCHECK_LT(message.size(), payload_size); - if (message.size() > 0) { - RTC_DCHECK(!partial_outgoing_message_.has_value()); - RTC_DLOG(LS_VERBOSE) << "Partially sent message. Buffering the remaining" - << message.size() << "/" << payload_size << " bytes."; - - partial_outgoing_message_.emplace(message); - } - return true; -} - -SendDataResult UsrsctpTransport::SendMessageInternal(OutgoingMessage* message) { - RTC_DCHECK_RUN_ON(network_thread_); - if (!sock_) { - RTC_LOG(LS_WARNING) << debug_name_ - << "->SendMessageInternal(...): " - "Not sending packet with sid=" - << message->sid() << " len=" << message->size() - << " before Start()."; - return SDR_ERROR; - } - if (message->send_params().type != webrtc::DataMessageType::kControl) { - auto it = stream_status_by_sid_.find(message->sid()); - if (it == stream_status_by_sid_.end()) { - RTC_LOG(LS_WARNING) << debug_name_ - << "->SendMessageInternal(...): " - "Not sending data because sid is unknown: " - << message->sid(); - return SDR_ERROR; - } - } - if (message->size() > static_cast(max_message_size_)) { - RTC_LOG(LS_ERROR) << "Attempting to send message of size " - << message->size() << " which is larger than limit " - << max_message_size_; - return SDR_ERROR; - } - - // Send data using SCTP. - sctp_sendv_spa spa = CreateSctpSendParams( - message->sid(), message->send_params(), message->size()); - const void* data = message->data(); - size_t data_length = message->size(); - if (message->size() == 0) { - // Empty messages are replaced by a single NUL byte on the wire as SCTP - // doesn't support empty messages. - // The PPID carries the information that the payload needs to be ignored. - data = kZero; - data_length = 1; - } - // Note: this send call is not atomic because the EOR bit is set. This means - // that usrsctp can partially accept this message and it is our duty to buffer - // the rest. - ssize_t send_res = usrsctp_sendv(sock_, data, data_length, NULL, 0, &spa, - rtc::checked_cast(sizeof(spa)), - SCTP_SENDV_SPA, 0); - if (send_res < 0) { - if (errno == SCTP_EWOULDBLOCK) { - ready_to_send_data_ = false; - RTC_LOG(LS_VERBOSE) << debug_name_ - << "->SendMessageInternal(...): EWOULDBLOCK returned"; - return SDR_BLOCK; - } - - RTC_LOG_ERRNO(LS_ERROR) << "ERROR:" << debug_name_ - << "->SendMessageInternal(...): " - " usrsctp_sendv: "; - return SDR_ERROR; - } - - size_t amount_sent = static_cast(send_res); - RTC_DCHECK_LE(amount_sent, data_length); - if (message->size() != 0) - message->Advance(amount_sent); - // Only way out now is success. - return SDR_SUCCESS; -} - -bool UsrsctpTransport::ReadyToSendData() { - RTC_DCHECK_RUN_ON(network_thread_); - return ready_to_send_data_; -} - -void UsrsctpTransport::ConnectTransportSignals() { - RTC_DCHECK_RUN_ON(network_thread_); - if (!transport_) { - return; - } - transport_->SignalWritableState.connect(this, - &UsrsctpTransport::OnWritableState); - transport_->SignalReadPacket.connect(this, &UsrsctpTransport::OnPacketRead); - transport_->SignalClosed.connect(this, &UsrsctpTransport::OnClosed); -} - -void UsrsctpTransport::DisconnectTransportSignals() { - RTC_DCHECK_RUN_ON(network_thread_); - if (!transport_) { - return; - } - transport_->SignalWritableState.disconnect(this); - transport_->SignalReadPacket.disconnect(this); - transport_->SignalClosed.disconnect(this); -} - -bool UsrsctpTransport::Connect() { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_LOG(LS_VERBOSE) << debug_name_ << "->Connect()."; - - // If we already have a socket connection (which shouldn't ever happen), just - // return. - RTC_DCHECK(!sock_); - if (sock_) { - RTC_LOG(LS_ERROR) << debug_name_ - << "->Connect(): Ignored as socket " - "is already established."; - return true; - } - - // If no socket (it was closed) try to start it again. This can happen when - // the socket we are connecting to closes, does an sctp shutdown handshake, - // or behaves unexpectedly causing us to perform a CloseSctpSocket. - if (!OpenSctpSocket()) { - return false; - } - - // Note: conversion from int to uint16_t happens on assignment. - sockaddr_conn local_sconn = GetSctpSockAddr(local_port_); - if (usrsctp_bind(sock_, reinterpret_cast(&local_sconn), - sizeof(local_sconn)) < 0) { - RTC_LOG_ERRNO(LS_ERROR) - << debug_name_ << "->Connect(): " << ("Failed usrsctp_bind"); - CloseSctpSocket(); - return false; - } - - // Note: conversion from int to uint16_t happens on assignment. - sockaddr_conn remote_sconn = GetSctpSockAddr(remote_port_); - int connect_result = usrsctp_connect( - sock_, reinterpret_cast(&remote_sconn), sizeof(remote_sconn)); - if (connect_result < 0 && errno != SCTP_EINPROGRESS) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->Connect(): " - "Failed usrsctp_connect. got errno=" - << errno << ", but wanted " << SCTP_EINPROGRESS; - CloseSctpSocket(); - return false; - } - // Set the MTU and disable MTU discovery. - // We can only do this after usrsctp_connect or it has no effect. - sctp_paddrparams params = {}; - memcpy(¶ms.spp_address, &remote_sconn, sizeof(remote_sconn)); - params.spp_flags = SPP_PMTUD_DISABLE; - // The MTU value provided specifies the space available for chunks in the - // packet, so we subtract the SCTP header size. - params.spp_pathmtu = kSctpMtu - sizeof(struct sctp_common_header); - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, ¶ms, - sizeof(params))) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->Connect(): " - "Failed to set SCTP_PEER_ADDR_PARAMS."; - } - // Since this is a fresh SCTP association, we'll always start out with empty - // queues, so "ReadyToSendData" should be true. - SetReadyToSendData(); - return true; -} - -bool UsrsctpTransport::OpenSctpSocket() { - RTC_DCHECK_RUN_ON(network_thread_); - if (sock_) { - RTC_LOG(LS_WARNING) << debug_name_ - << "->OpenSctpSocket(): " - "Ignoring attempt to re-create existing socket."; - return false; - } - - UsrSctpWrapper::IncrementUsrSctpUsageCount(); - - // If kSctpSendBufferSize isn't reflective of reality, we log an error, but we - // still have to do something reasonable here. Look up what the buffer's real - // size is and set our threshold to something reasonable. - // TODO(bugs.webrtc.org/11824): That was previously set to 50%, not 25%, but - // it was reduced to a recent usrsctp regression. Can return to 50% when the - // root cause is fixed. - static const int kSendThreshold = usrsctp_sysctl_get_sctp_sendspace() / 4; - - sock_ = usrsctp_socket( - AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &UsrSctpWrapper::OnSctpInboundPacket, - &UsrSctpWrapper::SendThresholdCallback, kSendThreshold, nullptr); - if (!sock_) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->OpenSctpSocket(): " - "Failed to create SCTP socket."; - UsrSctpWrapper::DecrementUsrSctpUsageCount(); - return false; - } - - if (!ConfigureSctpSocket()) { - usrsctp_close(sock_); - sock_ = nullptr; - UsrSctpWrapper::DecrementUsrSctpUsageCount(); - return false; - } - id_ = g_transport_map_->Register(this); - usrsctp_set_ulpinfo(sock_, reinterpret_cast(id_)); - // Register our id as an address for usrsctp. This is used by SCTP to - // direct the packets received (by the created socket) to this class. - usrsctp_register_address(reinterpret_cast(id_)); - return true; -} - -bool UsrsctpTransport::ConfigureSctpSocket() { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(sock_); - // Make the socket non-blocking. Connect, close, shutdown etc will not block - // the thread waiting for the socket operation to complete. - if (usrsctp_set_non_blocking(sock_, 1) < 0) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SCTP to non blocking."; - return false; - } - - // This ensures that the usrsctp close call deletes the association. This - // prevents usrsctp from calling OnSctpOutboundPacket with references to - // this class as the address. - linger linger_opt; - linger_opt.l_onoff = 1; - linger_opt.l_linger = 0; - if (usrsctp_setsockopt(sock_, SOL_SOCKET, SO_LINGER, &linger_opt, - sizeof(linger_opt))) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SO_LINGER."; - return false; - } - - // Enable stream ID resets. - struct sctp_assoc_value stream_rst; - stream_rst.assoc_id = SCTP_ALL_ASSOC; - stream_rst.assoc_value = 1; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, - &stream_rst, sizeof(stream_rst))) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SCTP_ENABLE_STREAM_RESET."; - return false; - } - - // Nagle. - uint32_t nodelay = 1; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, - sizeof(nodelay))) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SCTP_NODELAY."; - return false; - } - - // Explicit EOR. - uint32_t eor = 1; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &eor, - sizeof(eor))) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SCTP_EXPLICIT_EOR."; - return false; - } - - // Subscribe to SCTP event notifications. - // TODO(crbug.com/1137936): Subscribe to SCTP_SEND_FAILED_EVENT once deadlock - // is fixed upstream, or we switch to the upcall API: - // https://github.com/sctplab/usrsctp/issues/537 - int event_types[] = {SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE, - SCTP_SENDER_DRY_EVENT, SCTP_STREAM_RESET_EVENT}; - struct sctp_event event = {0}; - event.se_assoc_id = SCTP_ALL_ASSOC; - event.se_on = 1; - for (size_t i = 0; i < arraysize(event_types); i++) { - event.se_type = event_types[i]; - if (usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_EVENT, &event, - sizeof(event)) < 0) { - RTC_LOG_ERRNO(LS_ERROR) << debug_name_ - << "->ConfigureSctpSocket(): " - "Failed to set SCTP_EVENT type: " - << event.se_type; - return false; - } - } - return true; -} - -void UsrsctpTransport::CloseSctpSocket() { - RTC_DCHECK_RUN_ON(network_thread_); - if (sock_) { - // We assume that SO_LINGER option is set to close the association when - // close is called. This means that any pending packets in usrsctp will be - // discarded instead of being sent. - usrsctp_close(sock_); - sock_ = nullptr; - usrsctp_deregister_address(reinterpret_cast(id_)); - RTC_CHECK(g_transport_map_->Deregister(id_)); - UsrSctpWrapper::DecrementUsrSctpUsageCount(); - ready_to_send_data_ = false; - } -} - -bool UsrsctpTransport::SendQueuedStreamResets() { - RTC_DCHECK_RUN_ON(network_thread_); - - auto needs_reset = - [this](const std::map::value_type& stream) { - // Ignore streams with partial outgoing messages as they are required to - // be fully sent by the WebRTC spec - // https://w3c.github.io/webrtc-pc/#closing-procedure - return stream.second.need_outgoing_reset() && - (!partial_outgoing_message_.has_value() || - partial_outgoing_message_.value().sid() != - static_cast(stream.first)); - }; - // Figure out how many streams need to be reset. We need to do this so we can - // allocate the right amount of memory for the sctp_reset_streams structure. - size_t num_streams = absl::c_count_if(stream_status_by_sid_, needs_reset); - if (num_streams == 0) { - // Nothing to reset. - return true; - } - - RTC_LOG(LS_VERBOSE) << "SendQueuedStreamResets[" << debug_name_ - << "]: Resetting " << num_streams << " outgoing streams."; - - const size_t num_bytes = - sizeof(struct sctp_reset_streams) + (num_streams * sizeof(uint16_t)); - std::vector reset_stream_buf(num_bytes, 0); - struct sctp_reset_streams* resetp = - reinterpret_cast(&reset_stream_buf[0]); - resetp->srs_assoc_id = SCTP_ALL_ASSOC; - resetp->srs_flags = SCTP_STREAM_RESET_OUTGOING; - resetp->srs_number_streams = rtc::checked_cast(num_streams); - int result_idx = 0; - - for (const auto& stream : stream_status_by_sid_) { - if (needs_reset(stream)) { - resetp->srs_stream_list[result_idx++] = stream.first; - } - } - - int ret = - usrsctp_setsockopt(sock_, IPPROTO_SCTP, SCTP_RESET_STREAMS, resetp, - rtc::checked_cast(reset_stream_buf.size())); - if (ret < 0) { - // Note that usrsctp only lets us have one reset in progress at a time - // (even though multiple streams can be reset at once). If this happens, - // SendQueuedStreamResets will end up called after the current in-progress - // reset finishes, in OnStreamResetEvent. - RTC_LOG_ERRNO(LS_WARNING) << debug_name_ - << "->SendQueuedStreamResets(): " - "Failed to send a stream reset for " - << num_streams << " streams"; - return false; - } - - // Since the usrsctp call completed successfully, update our stream status - // map to note that we started the outgoing reset. - for (auto it = stream_status_by_sid_.begin(); - it != stream_status_by_sid_.end(); ++it) { - if (it->second.need_outgoing_reset()) { - it->second.outgoing_reset_initiated = true; - } - } - return true; -} - -void UsrsctpTransport::SetReadyToSendData() { - RTC_DCHECK_RUN_ON(network_thread_); - if (!ready_to_send_data_) { - ready_to_send_data_ = true; - SignalReadyToSendData(); - } -} - -bool UsrsctpTransport::SendBufferedMessage() { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(partial_outgoing_message_.has_value()); - RTC_DLOG(LS_VERBOSE) << "Sending partially buffered message of size " - << partial_outgoing_message_->size() << "."; - - SendMessageInternal(&partial_outgoing_message_.value()); - if (partial_outgoing_message_->size() > 0) { - // Still need to finish sending the message. - return false; - } - RTC_DCHECK_EQ(0u, partial_outgoing_message_->size()); - - int sid = partial_outgoing_message_->sid(); - partial_outgoing_message_.reset(); - - // Send the queued stream reset if it was pending for this stream. - auto it = stream_status_by_sid_.find(sid); - if (it->second.need_outgoing_reset()) { - SendQueuedStreamResets(); - } - - return true; -} - -void UsrsctpTransport::OnWritableState( - rtc::PacketTransportInternal* transport) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK_EQ(transport_, transport); - if (!was_ever_writable_ && transport->writable()) { - was_ever_writable_ = true; - if (started_) { - Connect(); - } - } -} - -// Called by network interface when a packet has been received. -void UsrsctpTransport::OnPacketRead(rtc::PacketTransportInternal* transport, - const char* data, - size_t len, - const int64_t& /* packet_time_us */, - int flags) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK_EQ(transport_, transport); - TRACE_EVENT0("webrtc", "UsrsctpTransport::OnPacketRead"); - - if (flags & PF_SRTP_BYPASS) { - // We are only interested in SCTP packets. - return; - } - - RTC_LOG(LS_VERBOSE) << debug_name_ - << "->OnPacketRead(...): " - " length=" - << len << ", started: " << started_; - // Only give receiving packets to usrsctp after if connected. This enables two - // peers to each make a connect call, but for them not to receive an INIT - // packet before they have called connect; least the last receiver of the INIT - // packet will have called connect, and a connection will be established. - if (sock_) { - // Pass received packet to SCTP stack. Once processed by usrsctp, the data - // will be will be given to the global OnSctpInboundPacket callback and - // posted to the transport thread. - VerboseLogPacket(data, len, SCTP_DUMP_INBOUND); - usrsctp_conninput(reinterpret_cast(id_), data, len, 0); - } else { - // TODO(ldixon): Consider caching the packet for very slightly better - // reliability. - } -} - -void UsrsctpTransport::OnClosed(rtc::PacketTransportInternal* transport) { - webrtc::RTCError error = - webrtc::RTCError(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA, - "Transport channel closed"); - error.set_error_detail(webrtc::RTCErrorDetailType::SCTP_FAILURE); - SignalClosedAbruptly(error); -} - -void UsrsctpTransport::OnSendThresholdCallback() { - RTC_DCHECK_RUN_ON(network_thread_); - if (partial_outgoing_message_.has_value()) { - if (!SendBufferedMessage()) { - // Did not finish sending the buffered message. - return; - } - } - SetReadyToSendData(); -} - -sockaddr_conn UsrsctpTransport::GetSctpSockAddr(int port) { - sockaddr_conn sconn = {0}; - sconn.sconn_family = AF_CONN; -#ifdef HAVE_SCONN_LEN - sconn.sconn_len = sizeof(sockaddr_conn); -#endif - // Note: conversion from int to uint16_t happens here. - sconn.sconn_port = rtc::HostToNetwork16(port); - sconn.sconn_addr = reinterpret_cast(id_); - return sconn; -} - -void UsrsctpTransport::OnPacketFromSctpToNetwork( - const rtc::CopyOnWriteBuffer& buffer) { - RTC_DCHECK_RUN_ON(network_thread_); - if (buffer.size() > (kSctpMtu)) { - RTC_LOG(LS_ERROR) << debug_name_ - << "->OnPacketFromSctpToNetwork(...): " - "SCTP seems to have made a packet that is bigger " - "than its official MTU: " - << buffer.size() << " vs max of " << kSctpMtu; - } - TRACE_EVENT0("webrtc", "UsrsctpTransport::OnPacketFromSctpToNetwork"); - - // Don't create noise by trying to send a packet when the DTLS transport isn't - // even writable. - if (!transport_ || !transport_->writable()) { - return; - } - - // Bon voyage. - transport_->SendPacket(buffer.data(), buffer.size(), - rtc::PacketOptions(), PF_NORMAL); -} - -void UsrsctpTransport::InjectDataOrNotificationFromSctpForTesting( - const void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags) { - OnDataOrNotificationFromSctp(data, length, rcv, flags); -} - -void UsrsctpTransport::OnDataOrNotificationFromSctp(const void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags) { - RTC_DCHECK_RUN_ON(network_thread_); - // If data is NULL, the SCTP association has been closed. - if (!data) { - RTC_LOG(LS_INFO) << debug_name_ - << "->OnDataOrNotificationFromSctp(...): " - "No data; association closed."; - return; - } - - // Handle notifications early. - // Note: Notifications are never split into chunks, so they can and should - // be handled early and entirely separate from the reassembly - // process. - if (flags & MSG_NOTIFICATION) { - RTC_LOG(LS_VERBOSE) - << debug_name_ - << "->OnDataOrNotificationFromSctp(...): SCTP notification" - << " length=" << length; - - rtc::CopyOnWriteBuffer notification(reinterpret_cast(data), - length); - OnNotificationFromSctp(notification); - return; - } - - // Log data chunk - const uint32_t ppid = rtc::NetworkToHost32(rcv.rcv_ppid); - RTC_LOG(LS_VERBOSE) << debug_name_ - << "->OnDataOrNotificationFromSctp(...): SCTP data chunk" - << " length=" << length << ", sid=" << rcv.rcv_sid - << ", ppid=" << ppid << ", ssn=" << rcv.rcv_ssn - << ", cum-tsn=" << rcv.rcv_cumtsn - << ", eor=" << ((flags & MSG_EOR) ? "y" : "n"); - - // Validate payload protocol identifier - webrtc::DataMessageType type; - if (!GetDataMediaType(ppid, &type)) { - // Unexpected PPID, dropping - RTC_LOG(LS_ERROR) << "Received an unknown PPID " << ppid - << " on an SCTP packet. Dropping."; - return; - } - - // Expect only continuation messages belonging to the same SID. The SCTP - // stack is expected to ensure this as long as the User Message - // Interleaving extension (RFC 8260) is not explicitly enabled, so this - // merely acts as a safeguard. - if ((partial_incoming_message_.size() != 0) && - (rcv.rcv_sid != partial_params_.sid)) { - RTC_LOG(LS_ERROR) << "Received a new SID without EOR in the previous" - << " SCTP packet. Discarding the previous packet."; - partial_incoming_message_.Clear(); - } - - // Copy metadata of interest - ReceiveDataParams params; - params.type = type; - params.sid = rcv.rcv_sid; - // Note that the SSN is identical for each chunk of the same message. - // Furthermore, it is increased per stream and not on the whole - // association. - params.seq_num = rcv.rcv_ssn; - - // Append the chunk's data to the message buffer unless we have a chunk with a - // PPID marking an empty message. - // See: https://tools.ietf.org/html/rfc8831#section-6.6 - if (!IsEmptyPPID(ppid)) - partial_incoming_message_.AppendData(reinterpret_cast(data), - length); - partial_params_ = params; - partial_flags_ = flags; - - // If the message is not yet complete... - if (!(flags & MSG_EOR)) { - if (partial_incoming_message_.size() < kSctpSendBufferSize) { - // We still have space in the buffer. Continue buffering chunks until - // the message is complete before handing it out. - return; - } else { - // The sender is exceeding the maximum message size that we announced. - // Spit out a warning but still hand out the partial message. Note that - // this behaviour is undesirable, see the discussion in issue 7774. - // - // TODO(lgrahl): Once sufficient time has passed and all supported - // browser versions obey the announced maximum message size, we should - // abort the SCTP association instead to prevent message integrity - // violation. - RTC_LOG(LS_ERROR) << "Handing out partial SCTP message."; - } - } - - // Dispatch the complete message and reset the message buffer. - OnDataFromSctpToTransport(params, partial_incoming_message_); - partial_incoming_message_.Clear(); -} - -void UsrsctpTransport::OnDataFromSctpToTransport( - const ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& buffer) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_LOG(LS_VERBOSE) << debug_name_ - << "->OnDataFromSctpToTransport(...): " - "Posting with length: " - << buffer.size() << " on stream " << params.sid; - // Reports all received messages to upper layers, no matter whether the sid - // is known. - SignalDataReceived(params, buffer); -} - -void UsrsctpTransport::OnNotificationFromSctp( - const rtc::CopyOnWriteBuffer& buffer) { - RTC_DCHECK_RUN_ON(network_thread_); - if (buffer.size() < sizeof(sctp_notification::sn_header)) { - RTC_LOG(LS_ERROR) << "SCTP notification is shorter than header size: " - << buffer.size(); - return; - } - - const sctp_notification& notification = - reinterpret_cast(*buffer.data()); - if (buffer.size() != notification.sn_header.sn_length) { - RTC_LOG(LS_ERROR) << "SCTP notification length (" << buffer.size() - << ") does not match sn_length field (" - << notification.sn_header.sn_length << ")."; - return; - } - - // TODO(ldixon): handle notifications appropriately. - switch (notification.sn_header.sn_type) { - case SCTP_ASSOC_CHANGE: - RTC_LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE"; - if (buffer.size() < sizeof(notification.sn_assoc_change)) { - RTC_LOG(LS_ERROR) - << "SCTP_ASSOC_CHANGE notification has less than required length: " - << buffer.size(); - return; - } - OnNotificationAssocChange(notification.sn_assoc_change); - break; - case SCTP_REMOTE_ERROR: - RTC_LOG(LS_INFO) << "SCTP_REMOTE_ERROR"; - break; - case SCTP_SHUTDOWN_EVENT: - RTC_LOG(LS_INFO) << "SCTP_SHUTDOWN_EVENT"; - break; - case SCTP_ADAPTATION_INDICATION: - RTC_LOG(LS_INFO) << "SCTP_ADAPTATION_INDICATION"; - break; - case SCTP_PARTIAL_DELIVERY_EVENT: - RTC_LOG(LS_INFO) << "SCTP_PARTIAL_DELIVERY_EVENT"; - break; - case SCTP_AUTHENTICATION_EVENT: - RTC_LOG(LS_INFO) << "SCTP_AUTHENTICATION_EVENT"; - break; - case SCTP_SENDER_DRY_EVENT: - RTC_LOG(LS_VERBOSE) << "SCTP_SENDER_DRY_EVENT"; - SetReadyToSendData(); - break; - // TODO(ldixon): Unblock after congestion. - case SCTP_NOTIFICATIONS_STOPPED_EVENT: - RTC_LOG(LS_INFO) << "SCTP_NOTIFICATIONS_STOPPED_EVENT"; - break; - case SCTP_SEND_FAILED_EVENT: { - if (buffer.size() < sizeof(notification.sn_send_failed_event)) { - RTC_LOG(LS_ERROR) << "SCTP_SEND_FAILED_EVENT notification has less " - "than required length: " - << buffer.size(); - return; - } - const struct sctp_send_failed_event& ssfe = - notification.sn_send_failed_event; - RTC_LOG(LS_WARNING) << "SCTP_SEND_FAILED_EVENT: message with" - " PPID = " - << rtc::NetworkToHost32(ssfe.ssfe_info.snd_ppid) - << " SID = " << ssfe.ssfe_info.snd_sid - << " flags = " << rtc::ToHex(ssfe.ssfe_info.snd_flags) - << " failed to sent due to error = " - << rtc::ToHex(ssfe.ssfe_error); - break; - } - case SCTP_STREAM_RESET_EVENT: - if (buffer.size() < sizeof(notification.sn_strreset_event)) { - RTC_LOG(LS_ERROR) << "SCTP_STREAM_RESET_EVENT notification has less " - "than required length: " - << buffer.size(); - return; - } - OnStreamResetEvent(¬ification.sn_strreset_event); - break; - case SCTP_ASSOC_RESET_EVENT: - RTC_LOG(LS_INFO) << "SCTP_ASSOC_RESET_EVENT"; - break; - case SCTP_STREAM_CHANGE_EVENT: - RTC_LOG(LS_INFO) << "SCTP_STREAM_CHANGE_EVENT"; - // An acknowledgment we get after our stream resets have gone through, - // if they've failed. We log the message, but don't react -- we don't - // keep around the last-transmitted set of SSIDs we wanted to close for - // error recovery. It doesn't seem likely to occur, and if so, likely - // harmless within the lifetime of a single SCTP association. - break; - case SCTP_PEER_ADDR_CHANGE: - RTC_LOG(LS_INFO) << "SCTP_PEER_ADDR_CHANGE"; - break; - default: - RTC_LOG(LS_WARNING) << "Unknown SCTP event: " - << notification.sn_header.sn_type; - break; - } -} - -void UsrsctpTransport::OnNotificationAssocChange( - const sctp_assoc_change& change) { - RTC_DCHECK_RUN_ON(network_thread_); - switch (change.sac_state) { - case SCTP_COMM_UP: - RTC_LOG(LS_VERBOSE) << "Association change SCTP_COMM_UP, stream # is " - << change.sac_outbound_streams << " outbound, " - << change.sac_inbound_streams << " inbound."; - max_outbound_streams_ = change.sac_outbound_streams; - max_inbound_streams_ = change.sac_inbound_streams; - SignalAssociationChangeCommunicationUp(); - // In case someone tried to close a stream before communication - // came up, send any queued resets. - SendQueuedStreamResets(); - break; - case SCTP_COMM_LOST: { - RTC_LOG(LS_INFO) << "Association change SCTP_COMM_LOST"; - webrtc::RTCError error = webrtc::RTCError( - webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA, - SctpErrorCauseCodeToString( - static_cast(change.sac_error))); - error.set_error_detail(webrtc::RTCErrorDetailType::SCTP_FAILURE); - error.set_sctp_cause_code(change.sac_error); - SignalClosedAbruptly(error); - break; - } - case SCTP_RESTART: - RTC_LOG(LS_INFO) << "Association change SCTP_RESTART"; - break; - case SCTP_SHUTDOWN_COMP: - RTC_LOG(LS_INFO) << "Association change SCTP_SHUTDOWN_COMP"; - break; - case SCTP_CANT_STR_ASSOC: - RTC_LOG(LS_INFO) << "Association change SCTP_CANT_STR_ASSOC"; - break; - default: - RTC_LOG(LS_INFO) << "Association change UNKNOWN"; - break; - } -} - -void UsrsctpTransport::OnStreamResetEvent( - const struct sctp_stream_reset_event* evt) { - RTC_DCHECK_RUN_ON(network_thread_); - - // This callback indicates that a reset is complete for incoming and/or - // outgoing streams. The reset may have been initiated by us or the remote - // side. - const int num_sids = (evt->strreset_length - sizeof(*evt)) / - sizeof(evt->strreset_stream_list[0]); - - if (evt->strreset_flags & SCTP_STREAM_RESET_FAILED) { - // OK, just try sending any previously sent stream resets again. The stream - // IDs sent over when the RESET_FIALED flag is set seem to be garbage - // values. Ignore them. - for (std::map::value_type& stream : - stream_status_by_sid_) { - stream.second.outgoing_reset_initiated = false; - } - SendQueuedStreamResets(); - // TODO(deadbeef): If this happens, the entire SCTP association is in quite - // crippled state. The SCTP session should be dismantled, and the WebRTC - // connectivity errored because is clear that the distant party is not - // playing ball: malforms the transported data. - return; - } - - // Loop over the received events and properly update the StreamStatus map. - for (int i = 0; i < num_sids; i++) { - const uint32_t sid = evt->strreset_stream_list[i]; - auto it = stream_status_by_sid_.find(sid); - if (it == stream_status_by_sid_.end()) { - // This stream is unknown. Sometimes this can be from a - // RESET_FAILED-related retransmit. - RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_EVENT(" << debug_name_ - << "): Unknown sid " << sid; - continue; - } - StreamStatus& status = it->second; - - if (evt->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { - RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_INCOMING_SSN(" << debug_name_ - << "): sid " << sid; - status.incoming_reset_complete = true; - // If we receive an incoming stream reset and we haven't started the - // closing procedure ourselves, this means the remote side started the - // closing procedure; fire a signal so that the relevant data channel - // can change to "closing" (we still need to reset the outgoing stream - // before it changes to "closed"). - if (!status.closure_initiated) { - SignalClosingProcedureStartedRemotely(sid); - } - } - if (evt->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { - RTC_LOG(LS_VERBOSE) << "SCTP_STREAM_RESET_OUTGOING_SSN(" << debug_name_ - << "): sid " << sid; - status.outgoing_reset_complete = true; - } - - // If this reset completes the closing procedure, remove the stream from - // our map so we can consider it closed, and fire a signal such that the - // relevant DataChannel will change its state to "closed" and its ID can be - // re-used. - if (status.reset_complete()) { - stream_status_by_sid_.erase(it); - SignalClosingProcedureComplete(sid); - } - } - - // Always try to send any queued resets because this call indicates that the - // last outgoing or incoming reset has made some progress. - SendQueuedStreamResets(); -} - -} // namespace cricket diff --git a/media/sctp/usrsctp_transport.h b/media/sctp/usrsctp_transport.h deleted file mode 100644 index 2dd6abf9c5..0000000000 --- a/media/sctp/usrsctp_transport.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef MEDIA_SCTP_USRSCTP_TRANSPORT_H_ -#define MEDIA_SCTP_USRSCTP_TRANSPORT_H_ - -#include - -#include -#include -#include -#include -#include -#include - -#include "absl/types/optional.h" -#include "rtc_base/buffer.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/task_utils/pending_task_safety_flag.h" -#include "rtc_base/third_party/sigslot/sigslot.h" -#include "rtc_base/thread.h" -// For SendDataParams/ReceiveDataParams. -#include "media/base/media_channel.h" -#include "media/sctp/sctp_transport_internal.h" - -// Defined by "usrsctplib/usrsctp.h" -struct sockaddr_conn; -struct sctp_assoc_change; -struct sctp_rcvinfo; -struct sctp_stream_reset_event; -struct sctp_sendv_spa; - -// Defined by -struct socket; -namespace cricket { - -// Holds data to be passed on to a transport. -struct SctpInboundPacket; - -// From transport calls, data flows like this: -// [network thread (although it can in princple be another thread)] -// 1. SctpTransport::SendData(data) -// 2. usrsctp_sendv(data) -// [network thread returns; sctp thread then calls the following] -// 3. OnSctpOutboundPacket(wrapped_data) -// [sctp thread returns having async invoked on the network thread] -// 4. SctpTransport::OnPacketFromSctpToNetwork(wrapped_data) -// 5. DtlsTransport::SendPacket(wrapped_data) -// 6. ... across network ... a packet is sent back ... -// 7. SctpTransport::OnPacketReceived(wrapped_data) -// 8. usrsctp_conninput(wrapped_data) -// [network thread returns; sctp thread then calls the following] -// 9. OnSctpInboundData(data) -// 10. SctpTransport::OnDataFromSctpToTransport(data) -// [sctp thread returns having async invoked on the network thread] -// 11. SctpTransport::OnDataFromSctpToTransport(data) -// 12. SctpTransport::SignalDataReceived(data) -// [from the same thread, methods registered/connected to -// SctpTransport are called with the recieved data] -class UsrsctpTransport : public SctpTransportInternal, - public sigslot::has_slots<> { - public: - // `network_thread` is where packets will be processed and callbacks from - // this transport will be posted, and is the only thread on which public - // methods can be called. - // `transport` is not required (can be null). - UsrsctpTransport(rtc::Thread* network_thread, - rtc::PacketTransportInternal* transport); - ~UsrsctpTransport() override; - - UsrsctpTransport(const UsrsctpTransport&) = delete; - UsrsctpTransport& operator=(const UsrsctpTransport&) = delete; - - // SctpTransportInternal overrides (see sctptransportinternal.h for comments). - void SetDtlsTransport(rtc::PacketTransportInternal* transport) override; - bool Start(int local_port, int remote_port, int max_message_size) override; - bool OpenStream(int sid) override; - bool ResetStream(int sid) override; - bool SendData(int sid, - const webrtc::SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result = nullptr) override; - bool ReadyToSendData() override; - int max_message_size() const override { return max_message_size_; } - absl::optional max_outbound_streams() const override { - return max_outbound_streams_; - } - absl::optional max_inbound_streams() const override { - return max_inbound_streams_; - } - void set_debug_name_for_testing(const char* debug_name) override { - debug_name_ = debug_name; - } - void InjectDataOrNotificationFromSctpForTesting(const void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags); - - // Exposed to allow Post call from c-callbacks. - // TODO(deadbeef): Remove this or at least make it return a const pointer. - rtc::Thread* network_thread() const { return network_thread_; } - - private: - // A message to be sent by the sctp library. This class is used to track the - // progress of writing a single message to the sctp library in the presence of - // partial writes. In this case, the Advance() function is provided in order - // to advance over what has already been accepted by the sctp library and - // avoid copying the remaining partial message buffer. - class OutgoingMessage { - public: - OutgoingMessage(const rtc::CopyOnWriteBuffer& buffer, - int sid, - const webrtc::SendDataParams& send_params) - : buffer_(buffer), sid_(sid), send_params_(send_params) {} - - // Advances the buffer by the incremented amount. Must not advance further - // than the current data size. - void Advance(size_t increment) { - RTC_DCHECK_LE(increment + offset_, buffer_.size()); - offset_ += increment; - } - - size_t size() const { return buffer_.size() - offset_; } - - const void* data() const { return buffer_.data() + offset_; } - - int sid() const { return sid_; } - webrtc::SendDataParams send_params() const { return send_params_; } - - private: - const rtc::CopyOnWriteBuffer buffer_; - int sid_; - const webrtc::SendDataParams send_params_; - size_t offset_ = 0; - }; - - void ConnectTransportSignals(); - void DisconnectTransportSignals(); - - // Creates the socket and connects. - bool Connect(); - - // Returns false when opening the socket failed. - bool OpenSctpSocket(); - // Helpet method to set socket options. - bool ConfigureSctpSocket(); - // Sets |sock_ |to nullptr. - void CloseSctpSocket(); - - // Sends a SCTP_RESET_STREAM for all streams in closing_ssids_. - bool SendQueuedStreamResets(); - - // Sets the "ready to send" flag and fires signal if needed. - void SetReadyToSendData(); - - // Sends the outgoing buffered message that was only partially accepted by the - // sctp lib because it did not have enough space. Returns true if the entire - // buffered message was accepted by the sctp lib. - bool SendBufferedMessage(); - - // Tries to send the `payload` on the usrsctp lib. The message will be - // advanced by the amount that was sent. - SendDataResult SendMessageInternal(OutgoingMessage* message); - - // Callbacks from DTLS transport. - void OnWritableState(rtc::PacketTransportInternal* transport); - virtual void OnPacketRead(rtc::PacketTransportInternal* transport, - const char* data, - size_t len, - const int64_t& packet_time_us, - int flags); - void OnClosed(rtc::PacketTransportInternal* transport); - - // Methods related to usrsctp callbacks. - void OnSendThresholdCallback(); - sockaddr_conn GetSctpSockAddr(int port); - - // Called using `invoker_` to send packet on the network. - void OnPacketFromSctpToNetwork(const rtc::CopyOnWriteBuffer& buffer); - - // Called on the network thread. - // Flags are standard socket API flags (RFC 6458). - void OnDataOrNotificationFromSctp(const void* data, - size_t length, - struct sctp_rcvinfo rcv, - int flags); - // Called using `invoker_` to decide what to do with the data. - void OnDataFromSctpToTransport(const ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& buffer); - // Called using `invoker_` to decide what to do with the notification. - void OnNotificationFromSctp(const rtc::CopyOnWriteBuffer& buffer); - void OnNotificationAssocChange(const sctp_assoc_change& change); - - void OnStreamResetEvent(const struct sctp_stream_reset_event* evt); - - // Responsible for marshalling incoming data to the transports listeners, and - // outgoing data to the network interface. - rtc::Thread* network_thread_; - // Helps pass inbound/outbound packets asynchronously to the network thread. - webrtc::ScopedTaskSafety task_safety_; - // Underlying DTLS transport. - rtc::PacketTransportInternal* transport_ = nullptr; - - // Track the data received from usrsctp between callbacks until the EOR bit - // arrives. - rtc::CopyOnWriteBuffer partial_incoming_message_; - ReceiveDataParams partial_params_; - int partial_flags_; - // A message that was attempted to be sent, but was only partially accepted by - // usrsctp lib with usrsctp_sendv() because it cannot buffer the full message. - // This occurs because we explicitly set the EOR bit when sending, so - // usrsctp_sendv() is not atomic. - absl::optional partial_outgoing_message_; - - bool was_ever_writable_ = false; - int local_port_ = kSctpDefaultPort; - int remote_port_ = kSctpDefaultPort; - int max_message_size_ = kSctpSendBufferSize; - struct socket* sock_ = nullptr; // The socket created by usrsctp_socket(...). - - // Has Start been called? Don't create SCTP socket until it has. - bool started_ = false; - // Are we ready to queue data (SCTP socket created, and not blocked due to - // congestion control)? Different than `transport_`'s "ready to send". - bool ready_to_send_data_ = false; - - // Used to keep track of the status of each stream (or rather, each pair of - // incoming/outgoing streams with matching IDs). It's specifically used to - // keep track of the status of resets, but more information could be put here - // later. - // - // See datachannel.h for a summary of the closing procedure. - struct StreamStatus { - // Closure initiated by application via ResetStream? Note that - // this may be true while outgoing_reset_initiated is false if the outgoing - // reset needed to be queued. - bool closure_initiated = false; - // Whether we've initiated the outgoing stream reset via - // SCTP_RESET_STREAMS. - bool outgoing_reset_initiated = false; - // Whether usrsctp has indicated that the incoming/outgoing streams have - // been reset. It's expected that the peer will reset its outgoing stream - // (our incoming stream) after receiving the reset for our outgoing stream, - // though older versions of chromium won't do this. See crbug.com/559394 - // for context. - bool outgoing_reset_complete = false; - bool incoming_reset_complete = false; - - // Some helper methods to improve code readability. - bool is_open() const { - return !closure_initiated && !incoming_reset_complete && - !outgoing_reset_complete; - } - // We need to send an outgoing reset if the application has closed the data - // channel, or if we received a reset of the incoming stream from the - // remote endpoint, indicating the data channel was closed remotely. - bool need_outgoing_reset() const { - return (incoming_reset_complete || closure_initiated) && - !outgoing_reset_initiated; - } - bool reset_complete() const { - return outgoing_reset_complete && incoming_reset_complete; - } - }; - - // Entries should only be removed from this map if `reset_complete` is - // true. - std::map stream_status_by_sid_; - - // A static human-readable name for debugging messages. - const char* debug_name_ = "UsrsctpTransport"; - // Hides usrsctp interactions from this header file. - class UsrSctpWrapper; - // Number of channels negotiated. Not set before negotiation completes. - absl::optional max_outbound_streams_; - absl::optional max_inbound_streams_; - - // Used for associating this transport with the underlying sctp socket in - // various callbacks. - uintptr_t id_ = 0; - - friend class UsrsctpTransportMap; -}; - -class UsrsctpTransportMap; - -} // namespace cricket - -#endif // MEDIA_SCTP_USRSCTP_TRANSPORT_H_ diff --git a/media/sctp/usrsctp_transport_reliability_unittest.cc b/media/sctp/usrsctp_transport_reliability_unittest.cc deleted file mode 100644 index 987dd04358..0000000000 --- a/media/sctp/usrsctp_transport_reliability_unittest.cc +++ /dev/null @@ -1,809 +0,0 @@ -/* - * 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 -#include -#include - -#include "media/sctp/sctp_transport_internal.h" -#include "media/sctp/usrsctp_transport.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/event.h" -#include "rtc_base/gunit.h" -#include "rtc_base/logging.h" -#include "rtc_base/random.h" -#include "rtc_base/synchronization/mutex.h" -#include "rtc_base/task_utils/pending_task_safety_flag.h" -#include "rtc_base/task_utils/to_queued_task.h" -#include "rtc_base/thread.h" -#include "test/gtest.h" - -namespace { - -static constexpr int kDefaultTimeout = 10000; // 10 seconds. -static constexpr int kTransport1Port = 15001; -static constexpr int kTransport2Port = 25002; -static constexpr int kLogPerMessagesCount = 100; - -/** - * An simple packet transport implementation which can be - * configured to simulate uniform random packet loss and - * configurable random packet delay and reordering. - */ -class SimulatedPacketTransport final : public rtc::PacketTransportInternal { - public: - SimulatedPacketTransport(std::string name, - rtc::Thread* transport_thread, - uint8_t packet_loss_percents, - uint16_t avg_send_delay_millis) - : transport_name_(name), - transport_thread_(transport_thread), - packet_loss_percents_(packet_loss_percents), - avg_send_delay_millis_(avg_send_delay_millis), - random_(42) { - RTC_DCHECK(transport_thread_); - RTC_DCHECK_LE(packet_loss_percents_, 100); - RTC_DCHECK_RUN_ON(transport_thread_); - } - - ~SimulatedPacketTransport() override { - RTC_DCHECK_RUN_ON(transport_thread_); - destination_ = nullptr; - SignalWritableState(this); - } - - SimulatedPacketTransport(const SimulatedPacketTransport&) = delete; - SimulatedPacketTransport& operator=(const SimulatedPacketTransport&) = delete; - - const std::string& transport_name() const override { return transport_name_; } - - bool writable() const override { return destination_ != nullptr; } - - bool receiving() const override { return true; } - - int SendPacket(const char* data, - size_t len, - const rtc::PacketOptions& options, - int flags = 0) { - RTC_DCHECK_RUN_ON(transport_thread_); - auto destination = destination_.load(); - if (destination == nullptr) { - return -1; - } - if (random_.Rand(100) < packet_loss_percents_) { - // silent packet loss - return 0; - } - rtc::CopyOnWriteBuffer buffer(data, len); - auto send_task = ToQueuedTask( - destination->task_safety_.flag(), - [destination, flags, buffer = std::move(buffer)] { - destination->SignalReadPacket( - destination, reinterpret_cast(buffer.data()), - buffer.size(), rtc::Time(), flags); - }); - // Introduce random send delay in range [0 .. 2 * avg_send_delay_millis_] - // millis, which will also work as random packet reordering mechanism. - uint16_t actual_send_delay = avg_send_delay_millis_; - int16_t reorder_delay = - avg_send_delay_millis_ * - std::min(1.0, std::max(-1.0, random_.Gaussian(0, 0.5))); - actual_send_delay += reorder_delay; - - if (actual_send_delay > 0) { - destination->transport_thread_->PostDelayedTask(std::move(send_task), - actual_send_delay); - } else { - destination->transport_thread_->PostTask(std::move(send_task)); - } - return 0; - } - - int SetOption(rtc::Socket::Option opt, int value) override { return 0; } - - bool GetOption(rtc::Socket::Option opt, int* value) override { return false; } - - int GetError() override { return 0; } - - absl::optional network_route() const override { - return absl::nullopt; - } - - void SetDestination(SimulatedPacketTransport* destination) { - RTC_DCHECK_RUN_ON(transport_thread_); - if (destination == this) { - return; - } - destination_ = destination; - SignalWritableState(this); - } - - private: - const std::string transport_name_; - rtc::Thread* const transport_thread_; - const uint8_t packet_loss_percents_; - const uint16_t avg_send_delay_millis_; - std::atomic destination_ ATOMIC_VAR_INIT(nullptr); - webrtc::Random random_; - webrtc::ScopedTaskSafety task_safety_; -}; - -/** - * A helper class to send specified number of messages over UsrsctpTransport - * with SCTP reliability settings provided by user. The reliability settings are - * specified by passing a template instance of SendDataParams. The sid will be - * assigned by sender itself and will be assigned from range - * [cricket::kMinSctpSid; cricket::kMaxSctpSid]. The wide range of sids are used - * to possibly trigger more execution paths inside usrsctp. - */ -class SctpDataSender final { - public: - SctpDataSender(rtc::Thread* thread, - cricket::UsrsctpTransport* transport, - uint64_t target_messages_count, - webrtc::SendDataParams send_params, - uint32_t sender_id) - : thread_(thread), - transport_(transport), - target_messages_count_(target_messages_count), - send_params_(send_params), - sender_id_(sender_id) { - RTC_DCHECK(thread_); - RTC_DCHECK(transport_); - } - - SctpDataSender(const SctpDataSender&) = delete; - SctpDataSender& operator=(const SctpDataSender&) = delete; - - void Start() { - thread_->PostTask(ToQueuedTask(task_safety_.flag(), [this] { - if (started_) { - RTC_LOG(LS_INFO) << sender_id_ << " sender is already started"; - return; - } - started_ = true; - SendNextMessage(); - })); - } - - uint64_t BytesSentCount() const { return num_bytes_sent_; } - - uint64_t MessagesSentCount() const { return num_messages_sent_; } - - absl::optional GetLastError() { - absl::optional result = absl::nullopt; - thread_->Invoke(RTC_FROM_HERE, - [this, &result] { result = last_error_; }); - return result; - } - - bool WaitForCompletion(int give_up_after_ms) { - return sent_target_messages_count_.Wait(give_up_after_ms, kDefaultTimeout); - } - - private: - void SendNextMessage() { - RTC_DCHECK_RUN_ON(thread_); - if (!started_ || num_messages_sent_ >= target_messages_count_) { - sent_target_messages_count_.Set(); - return; - } - - if (num_messages_sent_ % kLogPerMessagesCount == 0) { - RTC_LOG(LS_INFO) << sender_id_ << " sender will try send message " - << (num_messages_sent_ + 1) << " out of " - << target_messages_count_; - } - - webrtc::SendDataParams params(send_params_); - int sid = - cricket::kMinSctpSid + (num_messages_sent_ % cricket::kMaxSctpStreams); - - cricket::SendDataResult result; - transport_->SendData(sid, params, payload_, &result); - switch (result) { - case cricket::SDR_BLOCK: - // retry after timeout - thread_->PostDelayedTask( - ToQueuedTask(task_safety_.flag(), [this] { SendNextMessage(); }), - 500); - break; - case cricket::SDR_SUCCESS: - // send next - num_bytes_sent_ += payload_.size(); - ++num_messages_sent_; - thread_->PostTask( - ToQueuedTask(task_safety_.flag(), [this] { SendNextMessage(); })); - break; - case cricket::SDR_ERROR: - // give up - last_error_ = "UsrsctpTransport::SendData error returned"; - sent_target_messages_count_.Set(); - break; - } - } - - rtc::Thread* const thread_; - cricket::UsrsctpTransport* const transport_; - const uint64_t target_messages_count_; - const webrtc::SendDataParams send_params_; - const uint32_t sender_id_; - rtc::CopyOnWriteBuffer payload_{std::string(1400, '.').c_str(), 1400}; - std::atomic started_ ATOMIC_VAR_INIT(false); - std::atomic num_messages_sent_ ATOMIC_VAR_INIT(0); - rtc::Event sent_target_messages_count_{true, false}; - std::atomic num_bytes_sent_ ATOMIC_VAR_INIT(0); - absl::optional last_error_; - webrtc::ScopedTaskSafetyDetached task_safety_; -}; - -/** - * A helper class which counts number of received messages - * and bytes over UsrsctpTransport. Also allow waiting until - * specified number of messages received. - */ -class SctpDataReceiver final : public sigslot::has_slots<> { - public: - explicit SctpDataReceiver(uint32_t receiver_id, - uint64_t target_messages_count) - : receiver_id_(receiver_id), - target_messages_count_(target_messages_count) {} - - SctpDataReceiver(const SctpDataReceiver&) = delete; - SctpDataReceiver& operator=(const SctpDataReceiver&) = delete; - - void OnDataReceived(const cricket::ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& data) { - num_bytes_received_ += data.size(); - if (++num_messages_received_ == target_messages_count_) { - received_target_messages_count_.Set(); - } - - if (num_messages_received_ % kLogPerMessagesCount == 0) { - RTC_LOG(LS_INFO) << receiver_id_ << " receiver got " - << num_messages_received_ << " messages"; - } - } - - uint64_t MessagesReceivedCount() const { return num_messages_received_; } - - uint64_t BytesReceivedCount() const { return num_bytes_received_; } - - bool WaitForMessagesReceived(int timeout_millis) { - return received_target_messages_count_.Wait(timeout_millis); - } - - private: - std::atomic num_messages_received_ ATOMIC_VAR_INIT(0); - std::atomic num_bytes_received_ ATOMIC_VAR_INIT(0); - rtc::Event received_target_messages_count_{true, false}; - const uint32_t receiver_id_; - const uint64_t target_messages_count_; -}; - -/** - * Simple class to manage set of threads. - */ -class ThreadPool final { - public: - explicit ThreadPool(size_t threads_count) : random_(42) { - RTC_DCHECK(threads_count > 0); - threads_.reserve(threads_count); - for (size_t i = 0; i < threads_count; i++) { - auto thread = rtc::Thread::Create(); - thread->SetName("Thread #" + rtc::ToString(i + 1) + " from Pool", this); - thread->Start(); - threads_.emplace_back(std::move(thread)); - } - } - - ThreadPool(const ThreadPool&) = delete; - ThreadPool& operator=(const ThreadPool&) = delete; - - rtc::Thread* GetRandomThread() { - return threads_[random_.Rand(0U, threads_.size() - 1)].get(); - } - - private: - webrtc::Random random_; - std::vector> threads_; -}; - -/** - * Represents single ping-pong test over UsrsctpTransport. - * User can specify target number of message for bidirectional - * send, underlying transport packets loss and average packet delay - * and SCTP delivery settings. - */ -class SctpPingPong final { - public: - SctpPingPong(uint32_t id, - uint16_t port1, - uint16_t port2, - rtc::Thread* transport_thread1, - rtc::Thread* transport_thread2, - uint32_t messages_count, - uint8_t packet_loss_percents, - uint16_t avg_send_delay_millis, - webrtc::SendDataParams send_params) - : id_(id), - port1_(port1), - port2_(port2), - transport_thread1_(transport_thread1), - transport_thread2_(transport_thread2), - messages_count_(messages_count), - packet_loss_percents_(packet_loss_percents), - avg_send_delay_millis_(avg_send_delay_millis), - send_params_(send_params) { - RTC_DCHECK(transport_thread1_ != nullptr); - RTC_DCHECK(transport_thread2_ != nullptr); - } - - virtual ~SctpPingPong() { - transport_thread1_->Invoke(RTC_FROM_HERE, [this] { - data_sender1_.reset(); - sctp_transport1_->SetDtlsTransport(nullptr); - packet_transport1_->SetDestination(nullptr); - }); - transport_thread2_->Invoke(RTC_FROM_HERE, [this] { - data_sender2_.reset(); - sctp_transport2_->SetDtlsTransport(nullptr); - packet_transport2_->SetDestination(nullptr); - }); - transport_thread1_->Invoke(RTC_FROM_HERE, [this] { - sctp_transport1_.reset(); - data_receiver1_.reset(); - packet_transport1_.reset(); - }); - transport_thread2_->Invoke(RTC_FROM_HERE, [this] { - sctp_transport2_.reset(); - data_receiver2_.reset(); - packet_transport2_.reset(); - }); - } - - SctpPingPong(const SctpPingPong&) = delete; - SctpPingPong& operator=(const SctpPingPong&) = delete; - - bool Start() { - CreateTwoConnectedSctpTransportsWithAllStreams(); - - { - webrtc::MutexLock lock(&lock_); - if (!errors_list_.empty()) { - return false; - } - } - - data_sender1_.reset(new SctpDataSender(transport_thread1_, - sctp_transport1_.get(), - messages_count_, send_params_, id_)); - data_sender2_.reset(new SctpDataSender(transport_thread2_, - sctp_transport2_.get(), - messages_count_, send_params_, id_)); - data_sender1_->Start(); - data_sender2_->Start(); - return true; - } - - std::vector GetErrorsList() const { - std::vector result; - { - webrtc::MutexLock lock(&lock_); - result = errors_list_; - } - return result; - } - - void WaitForCompletion(int32_t timeout_millis) { - if (data_sender1_ == nullptr) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 1 is not created"); - return; - } - if (data_sender2_ == nullptr) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 2 is not created"); - return; - } - - if (!data_sender1_->WaitForCompletion(timeout_millis)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 1 failed to complete within " + - rtc::ToString(timeout_millis) + " millis"); - return; - } - - auto sender1_error = data_sender1_->GetLastError(); - if (sender1_error.has_value()) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 1 error: " + sender1_error.value()); - return; - } - - if (!data_sender2_->WaitForCompletion(timeout_millis)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 2 failed to complete within " + - rtc::ToString(timeout_millis) + " millis"); - return; - } - - auto sender2_error = data_sender2_->GetLastError(); - if (sender2_error.has_value()) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 2 error: " + sender1_error.value()); - return; - } - - if ((data_sender1_->MessagesSentCount() != messages_count_)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 1 sent only " + - rtc::ToString(data_sender1_->MessagesSentCount()) + - " out of " + rtc::ToString(messages_count_)); - return; - } - - if ((data_sender2_->MessagesSentCount() != messages_count_)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sender 2 sent only " + - rtc::ToString(data_sender2_->MessagesSentCount()) + - " out of " + rtc::ToString(messages_count_)); - return; - } - - if (!data_receiver1_->WaitForMessagesReceived(timeout_millis)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", receiver 1 did not complete within " + - rtc::ToString(messages_count_)); - return; - } - - if (!data_receiver2_->WaitForMessagesReceived(timeout_millis)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", receiver 2 did not complete within " + - rtc::ToString(messages_count_)); - return; - } - - if (data_receiver1_->BytesReceivedCount() != - data_sender2_->BytesSentCount()) { - ReportError( - "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 1 received " + - rtc::ToString(data_receiver1_->BytesReceivedCount()) + - " bytes, but sender 2 send " + - rtc::ToString(rtc::ToString(data_sender2_->BytesSentCount()))); - return; - } - - if (data_receiver2_->BytesReceivedCount() != - data_sender1_->BytesSentCount()) { - ReportError( - "SctpPingPong id = " + rtc::ToString(id_) + ", receiver 2 received " + - rtc::ToString(data_receiver2_->BytesReceivedCount()) + - " bytes, but sender 1 send " + - rtc::ToString(rtc::ToString(data_sender1_->BytesSentCount()))); - return; - } - - RTC_LOG(LS_INFO) << "SctpPingPong id = " << id_ << " is done"; - } - - private: - void CreateTwoConnectedSctpTransportsWithAllStreams() { - transport_thread1_->Invoke(RTC_FROM_HERE, [this] { - packet_transport1_.reset(new SimulatedPacketTransport( - "SctpPingPong id = " + rtc::ToString(id_) + ", packet transport 1", - transport_thread1_, packet_loss_percents_, avg_send_delay_millis_)); - data_receiver1_.reset(new SctpDataReceiver(id_, messages_count_)); - sctp_transport1_.reset(new cricket::UsrsctpTransport( - transport_thread1_, packet_transport1_.get())); - sctp_transport1_->set_debug_name_for_testing("sctp transport 1"); - - sctp_transport1_->SignalDataReceived.connect( - data_receiver1_.get(), &SctpDataReceiver::OnDataReceived); - - for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) { - if (!sctp_transport1_->OpenStream(i)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sctp transport 1 stream " + rtc::ToString(i) + - " failed to open"); - break; - } - } - }); - - transport_thread2_->Invoke(RTC_FROM_HERE, [this] { - packet_transport2_.reset(new SimulatedPacketTransport( - "SctpPingPong id = " + rtc::ToString(id_) + "packet transport 2", - transport_thread2_, packet_loss_percents_, avg_send_delay_millis_)); - data_receiver2_.reset(new SctpDataReceiver(id_, messages_count_)); - sctp_transport2_.reset(new cricket::UsrsctpTransport( - transport_thread2_, packet_transport2_.get())); - sctp_transport2_->set_debug_name_for_testing("sctp transport 2"); - sctp_transport2_->SignalDataReceived.connect( - data_receiver2_.get(), &SctpDataReceiver::OnDataReceived); - - for (uint32_t i = cricket::kMinSctpSid; i <= cricket::kMaxSctpSid; i++) { - if (!sctp_transport2_->OpenStream(i)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", sctp transport 2 stream " + rtc::ToString(i) + - " failed to open"); - break; - } - } - }); - - transport_thread1_->Invoke(RTC_FROM_HERE, [this] { - packet_transport1_->SetDestination(packet_transport2_.get()); - }); - transport_thread2_->Invoke(RTC_FROM_HERE, [this] { - packet_transport2_->SetDestination(packet_transport1_.get()); - }); - - transport_thread1_->Invoke(RTC_FROM_HERE, [this] { - if (!sctp_transport1_->Start(port1_, port2_, - cricket::kSctpSendBufferSize)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", failed to start sctp transport 1"); - } - }); - - transport_thread2_->Invoke(RTC_FROM_HERE, [this] { - if (!sctp_transport2_->Start(port2_, port1_, - cricket::kSctpSendBufferSize)) { - ReportError("SctpPingPong id = " + rtc::ToString(id_) + - ", failed to start sctp transport 2"); - } - }); - } - - void ReportError(std::string error) { - webrtc::MutexLock lock(&lock_); - errors_list_.push_back(std::move(error)); - } - - std::unique_ptr packet_transport1_; - std::unique_ptr packet_transport2_; - std::unique_ptr data_receiver1_; - std::unique_ptr data_receiver2_; - std::unique_ptr sctp_transport1_; - std::unique_ptr sctp_transport2_; - std::unique_ptr data_sender1_; - std::unique_ptr data_sender2_; - mutable webrtc::Mutex lock_; - std::vector errors_list_ RTC_GUARDED_BY(lock_); - - const uint32_t id_; - const uint16_t port1_; - const uint16_t port2_; - rtc::Thread* const transport_thread1_; - rtc::Thread* const transport_thread2_; - const uint32_t messages_count_; - const uint8_t packet_loss_percents_; - const uint16_t avg_send_delay_millis_; - const webrtc::SendDataParams send_params_; -}; - -/** - * Helper function to calculate max number of milliseconds - * allowed for test to run based on test configuration. - */ -constexpr int32_t GetExecutionTimeLimitInMillis(uint32_t total_messages, - uint8_t packet_loss_percents) { - return std::min( - std::numeric_limits::max(), - std::max( - 1LL * total_messages * 100 * - std::max(1, packet_loss_percents * packet_loss_percents), - kDefaultTimeout)); -} - -} // namespace - -namespace cricket { - -/** - * The set of tests intended to check usrsctp reliability on - * stress conditions: multiple sockets, concurrent access, - * lossy network link. It was observed in the past that - * usrsctp might misbehave in concurrent environment - * under load on lossy networks: deadlocks and memory corruption - * issues might happen in non-basic usage scenarios. - * It's recommended to run this test whenever usrsctp version - * used is updated to verify it properly works in stress - * conditions under higher than usual load. - * It is also recommended to enable ASAN when these tests - * are executed, so whenever memory bug is happen inside usrsctp, - * it will be easier to understand what went wrong with ASAN - * provided diagnostics information. - * The tests cases currently disabled by default due to - * long execution time and due to unresolved issue inside - * `usrsctp` library detected by try-bots with ThreadSanitizer. - */ -class UsrSctpReliabilityTest : public ::testing::Test {}; - -/** - * A simple test which send multiple messages over reliable - * connection, usefull to verify test infrastructure works. - * Execution time is less than 1 second. - */ -TEST_F(UsrSctpReliabilityTest, - DISABLED_AllMessagesAreDeliveredOverReliableConnection) { - auto thread1 = rtc::Thread::Create(); - auto thread2 = rtc::Thread::Create(); - thread1->Start(); - thread2->Start(); - constexpr uint8_t packet_loss_percents = 0; - constexpr uint16_t avg_send_delay_millis = 10; - constexpr uint32_t messages_count = 100; - constexpr int32_t wait_timeout = - GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents); - static_assert(wait_timeout > 0, - "Timeout computation must produce positive value"); - - webrtc::SendDataParams send_params; - send_params.ordered = true; - - SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(), - thread2.get(), messages_count, packet_loss_percents, - avg_send_delay_millis, send_params); - EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';'); - test.WaitForCompletion(wait_timeout); - auto errors_list = test.GetErrorsList(); - EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';'); -} - -/** - * A test to verify that multiple messages can be reliably delivered - * over lossy network when usrsctp configured to guarantee reliably - * and in order delivery. - * The test case is disabled by default because it takes - * long time to run. - * Execution time is about 2.5 minutes. - */ -TEST_F(UsrSctpReliabilityTest, - DISABLED_AllMessagesAreDeliveredOverLossyConnectionReliableAndInOrder) { - auto thread1 = rtc::Thread::Create(); - auto thread2 = rtc::Thread::Create(); - thread1->Start(); - thread2->Start(); - constexpr uint8_t packet_loss_percents = 5; - constexpr uint16_t avg_send_delay_millis = 16; - constexpr uint32_t messages_count = 10000; - constexpr int32_t wait_timeout = - GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents); - static_assert(wait_timeout > 0, - "Timeout computation must produce positive value"); - - webrtc::SendDataParams send_params; - send_params.ordered = true; - - SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(), - thread2.get(), messages_count, packet_loss_percents, - avg_send_delay_millis, send_params); - - EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';'); - test.WaitForCompletion(wait_timeout); - auto errors_list = test.GetErrorsList(); - EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';'); -} - -/** - * A test to verify that multiple messages can be reliably delivered - * over lossy network when usrsctp configured to retransmit lost - * packets. - * The test case is disabled by default because it takes - * long time to run. - * Execution time is about 2.5 minutes. - */ -TEST_F(UsrSctpReliabilityTest, - DISABLED_AllMessagesAreDeliveredOverLossyConnectionWithRetries) { - auto thread1 = rtc::Thread::Create(); - auto thread2 = rtc::Thread::Create(); - thread1->Start(); - thread2->Start(); - constexpr uint8_t packet_loss_percents = 5; - constexpr uint16_t avg_send_delay_millis = 16; - constexpr uint32_t messages_count = 10000; - constexpr int32_t wait_timeout = - GetExecutionTimeLimitInMillis(messages_count, packet_loss_percents); - static_assert(wait_timeout > 0, - "Timeout computation must produce positive value"); - - webrtc::SendDataParams send_params; - send_params.ordered = false; - send_params.max_rtx_count = std::numeric_limits::max(); - send_params.max_rtx_ms = std::numeric_limits::max(); - - SctpPingPong test(1, kTransport1Port, kTransport2Port, thread1.get(), - thread2.get(), messages_count, packet_loss_percents, - avg_send_delay_millis, send_params); - - EXPECT_TRUE(test.Start()) << rtc::join(test.GetErrorsList(), ';'); - test.WaitForCompletion(wait_timeout); - auto errors_list = test.GetErrorsList(); - EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';'); -} - -/** - * This is kind of reliability stress-test of usrsctp to verify - * that all messages are delivered when multiple usrsctp - * sockets used concurrently and underlying transport is lossy. - * - * It was observed in the past that in stress condtions usrsctp - * might encounter deadlock and memory corruption bugs: - * https://github.com/sctplab/usrsctp/issues/325 - * - * It is recoomended to run this test whenever usrsctp version - * used by WebRTC is updated. - * - * The test case is disabled by default because it takes - * long time to run. - * Execution time of this test is about 1-2 hours. - */ -TEST_F(UsrSctpReliabilityTest, - DISABLED_AllMessagesAreDeliveredOverLossyConnectionConcurrentTests) { - ThreadPool pool(16); - - webrtc::SendDataParams send_params; - send_params.ordered = true; - constexpr uint32_t base_sctp_port = 5000; - - // The constants value below were experimentally chosen - // to have reasonable execution time and to reproduce - // particular deadlock issue inside usrsctp: - // https://github.com/sctplab/usrsctp/issues/325 - // The constants values may be adjusted next time - // some other issue inside usrsctp need to be debugged. - constexpr uint32_t messages_count = 200; - constexpr uint8_t packet_loss_percents = 5; - constexpr uint16_t avg_send_delay_millis = 0; - constexpr uint32_t parallel_ping_pongs = 16 * 1024; - constexpr uint32_t total_ping_pong_tests = 16 * parallel_ping_pongs; - - constexpr int32_t wait_timeout = GetExecutionTimeLimitInMillis( - total_ping_pong_tests * messages_count, packet_loss_percents); - static_assert(wait_timeout > 0, - "Timeout computation must produce positive value"); - - std::queue> tests; - - for (uint32_t i = 0; i < total_ping_pong_tests; i++) { - uint32_t port1 = - base_sctp_port + (2 * i) % (UINT16_MAX - base_sctp_port - 1); - - auto test = std::make_unique( - i, port1, port1 + 1, pool.GetRandomThread(), pool.GetRandomThread(), - messages_count, packet_loss_percents, avg_send_delay_millis, - send_params); - - EXPECT_TRUE(test->Start()) << rtc::join(test->GetErrorsList(), ';'); - tests.emplace(std::move(test)); - - while (tests.size() >= parallel_ping_pongs) { - auto& oldest_test = tests.front(); - oldest_test->WaitForCompletion(wait_timeout); - - auto errors_list = oldest_test->GetErrorsList(); - EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';'); - tests.pop(); - } - } - - while (!tests.empty()) { - auto& oldest_test = tests.front(); - oldest_test->WaitForCompletion(wait_timeout); - - auto errors_list = oldest_test->GetErrorsList(); - EXPECT_TRUE(errors_list.empty()) << rtc::join(errors_list, ';'); - tests.pop(); - } -} - -} // namespace cricket diff --git a/media/sctp/usrsctp_transport_unittest.cc b/media/sctp/usrsctp_transport_unittest.cc deleted file mode 100644 index 59e9c59b3d..0000000000 --- a/media/sctp/usrsctp_transport_unittest.cc +++ /dev/null @@ -1,883 +0,0 @@ -/* - * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "media/sctp/usrsctp_transport.h" - -#include -#include -#include - -#include -#include -#include - -#include "absl/algorithm/container.h" -#include "media/sctp/sctp_transport_internal.h" -#include "p2p/base/fake_dtls_transport.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/gunit.h" -#include "rtc_base/logging.h" -#include "rtc_base/thread.h" -#include "test/gtest.h" - -namespace { -static const int kDefaultTimeout = 10000; // 10 seconds. -// Use ports other than the default 5000 for testing. -static const int kTransport1Port = 5001; -static const int kTransport2Port = 5002; -} // namespace - -namespace cricket { - -// This is essentially a buffer to hold recieved data. It stores only the last -// received data. Calling OnDataReceived twice overwrites old data with the -// newer one. -// TODO(ldixon): Implement constraints, and allow new data to be added to old -// instead of replacing it. -class SctpFakeDataReceiver : public sigslot::has_slots<> { - public: - SctpFakeDataReceiver() : received_(false) {} - - void Clear() { - received_ = false; - last_data_ = ""; - last_params_ = ReceiveDataParams(); - num_messages_received_ = 0; - } - - void OnDataReceived(const ReceiveDataParams& params, - const rtc::CopyOnWriteBuffer& data) { - num_messages_received_++; - received_ = true; - last_data_ = std::string(data.data(), data.size()); - last_params_ = params; - } - - bool received() const { return received_; } - std::string last_data() const { return last_data_; } - ReceiveDataParams last_params() const { return last_params_; } - size_t num_messages_received() const { return num_messages_received_; } - - private: - bool received_; - std::string last_data_; - size_t num_messages_received_ = 0; - ReceiveDataParams last_params_; -}; - -class SctpTransportObserver : public sigslot::has_slots<> { - public: - explicit SctpTransportObserver(UsrsctpTransport* transport) { - transport->SignalClosingProcedureComplete.connect( - this, &SctpTransportObserver::OnClosingProcedureComplete); - transport->SignalReadyToSendData.connect( - this, &SctpTransportObserver::OnReadyToSend); - } - - int StreamCloseCount(int stream) { - return absl::c_count(closed_streams_, stream); - } - - bool WasStreamClosed(int stream) { - return absl::c_linear_search(closed_streams_, stream); - } - - bool ReadyToSend() { return ready_to_send_; } - - private: - void OnClosingProcedureComplete(int stream) { - closed_streams_.push_back(stream); - } - void OnReadyToSend() { ready_to_send_ = true; } - - std::vector closed_streams_; - bool ready_to_send_ = false; -}; - -// Helper class used to immediately attempt to reopen a stream as soon as it's -// been closed. -class SignalTransportClosedReopener : public sigslot::has_slots<> { - public: - SignalTransportClosedReopener(UsrsctpTransport* transport, - UsrsctpTransport* peer) - : transport_(transport), peer_(peer) {} - - int StreamCloseCount(int stream) { return absl::c_count(streams_, stream); } - - private: - void OnStreamClosed(int stream) { - transport_->OpenStream(stream); - peer_->OpenStream(stream); - streams_.push_back(stream); - } - - UsrsctpTransport* transport_; - UsrsctpTransport* peer_; - std::vector streams_; -}; - -// SCTP Data Engine testing framework. -class SctpTransportTest : public ::testing::Test, public sigslot::has_slots<> { - protected: - // usrsctp uses the NSS random number generator on non-Android platforms, - // so we need to initialize SSL. - static void SetUpTestSuite() {} - - void SetupConnectedTransportsWithTwoStreams() { - SetupConnectedTransportsWithTwoStreams(kTransport1Port, kTransport2Port); - } - - void SetupConnectedTransportsWithTwoStreams(int port1, int port2) { - fake_dtls1_.reset(new FakeDtlsTransport("fake dtls 1", 0)); - fake_dtls2_.reset(new FakeDtlsTransport("fake dtls 2", 0)); - recv1_.reset(new SctpFakeDataReceiver()); - recv2_.reset(new SctpFakeDataReceiver()); - transport1_.reset(CreateTransport(fake_dtls1_.get(), recv1_.get())); - transport1_->set_debug_name_for_testing("transport1"); - transport1_->SignalReadyToSendData.connect( - this, &SctpTransportTest::OnChan1ReadyToSend); - transport2_.reset(CreateTransport(fake_dtls2_.get(), recv2_.get())); - transport2_->set_debug_name_for_testing("transport2"); - transport2_->SignalReadyToSendData.connect( - this, &SctpTransportTest::OnChan2ReadyToSend); - // Setup two connected transports ready to send and receive. - bool asymmetric = false; - fake_dtls1_->SetDestination(fake_dtls2_.get(), asymmetric); - - RTC_LOG(LS_VERBOSE) << "Transport setup ----------------------------- "; - AddStream(1); - AddStream(2); - - RTC_LOG(LS_VERBOSE) - << "Connect the transports -----------------------------"; - // Both transports need to have started (with matching ports) for an - // association to be formed. - transport1_->Start(port1, port2, kSctpSendBufferSize); - transport2_->Start(port2, port1, kSctpSendBufferSize); - } - - bool AddStream(int sid) { - bool ret = true; - ret = ret && transport1_->OpenStream(sid); - ret = ret && transport2_->OpenStream(sid); - return ret; - } - - UsrsctpTransport* CreateTransport(FakeDtlsTransport* fake_dtls, - SctpFakeDataReceiver* recv) { - UsrsctpTransport* transport = - new UsrsctpTransport(rtc::Thread::Current(), fake_dtls); - // When data is received, pass it to the SctpFakeDataReceiver. - transport->SignalDataReceived.connect( - recv, &SctpFakeDataReceiver::OnDataReceived); - return transport; - } - - bool SendData(UsrsctpTransport* chan, - int sid, - const std::string& msg, - SendDataResult* result, - bool ordered = false) { - webrtc::SendDataParams params; - params.ordered = ordered; - - return chan->SendData( - sid, params, rtc::CopyOnWriteBuffer(&msg[0], msg.length()), result); - } - - bool ReceivedData(const SctpFakeDataReceiver* recv, - int sid, - const std::string& msg) { - return (recv->received() && recv->last_params().sid == sid && - recv->last_data() == msg); - } - - bool ProcessMessagesUntilIdle() { - rtc::Thread* thread = rtc::Thread::Current(); - while (!thread->empty()) { - rtc::Message msg; - if (thread->Get(&msg, rtc::Thread::kForever)) { - thread->Dispatch(&msg); - } - } - return !thread->IsQuitting(); - } - - UsrsctpTransport* transport1() { return transport1_.get(); } - UsrsctpTransport* transport2() { return transport2_.get(); } - SctpFakeDataReceiver* receiver1() { return recv1_.get(); } - SctpFakeDataReceiver* receiver2() { return recv2_.get(); } - FakeDtlsTransport* fake_dtls1() { return fake_dtls1_.get(); } - FakeDtlsTransport* fake_dtls2() { return fake_dtls2_.get(); } - - int transport1_ready_to_send_count() { - return transport1_ready_to_send_count_; - } - int transport2_ready_to_send_count() { - return transport2_ready_to_send_count_; - } - - private: - std::unique_ptr fake_dtls1_; - std::unique_ptr fake_dtls2_; - std::unique_ptr recv1_; - std::unique_ptr recv2_; - std::unique_ptr transport1_; - std::unique_ptr transport2_; - - int transport1_ready_to_send_count_ = 0; - int transport2_ready_to_send_count_ = 0; - - void OnChan1ReadyToSend() { ++transport1_ready_to_send_count_; } - void OnChan2ReadyToSend() { ++transport2_ready_to_send_count_; } -}; - -TEST_F(SctpTransportTest, MessageInterleavedWithNotification) { - FakeDtlsTransport fake_dtls1("fake dtls 1", 0); - FakeDtlsTransport fake_dtls2("fake dtls 2", 0); - SctpFakeDataReceiver recv1; - SctpFakeDataReceiver recv2; - std::unique_ptr transport1( - CreateTransport(&fake_dtls1, &recv1)); - std::unique_ptr transport2( - CreateTransport(&fake_dtls2, &recv2)); - - // Add a stream. - transport1->OpenStream(1); - transport2->OpenStream(1); - - // Start SCTP transports. - transport1->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize); - transport2->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize); - - // Connect the two fake DTLS transports. - fake_dtls1.SetDestination(&fake_dtls2, false); - - // Ensure the SCTP association has been established - // Note: I'd rather watch for an assoc established state here but couldn't - // find any exposed... - SendDataResult result; - ASSERT_TRUE(SendData(transport2.get(), 1, "meow", &result)); - EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "meow"), kDefaultTimeout); - - // Detach the DTLS transport to ensure only we will inject packets from here - // on. - transport1->SetDtlsTransport(nullptr); - - // Prepare chunk buffer and metadata - auto chunk = rtc::CopyOnWriteBuffer(32); - struct sctp_rcvinfo meta = {0}; - meta.rcv_sid = 1; - meta.rcv_ssn = 1337; - meta.rcv_ppid = rtc::HostToNetwork32(51); // text (complete) - - // Inject chunk 1/2. - meta.rcv_tsn = 42; - meta.rcv_cumtsn = 42; - chunk.SetData("meow?", 5); - transport1->InjectDataOrNotificationFromSctpForTesting(chunk.data(), - chunk.size(), meta, 0); - - // Inject a notification in between chunks. - union sctp_notification notification; - memset(¬ification, 0, sizeof(notification)); - // Type chosen since it's not handled apart from being logged - notification.sn_header.sn_type = SCTP_PEER_ADDR_CHANGE; - notification.sn_header.sn_flags = 0; - notification.sn_header.sn_length = sizeof(notification); - transport1->InjectDataOrNotificationFromSctpForTesting( - ¬ification, sizeof(notification), {0}, MSG_NOTIFICATION); - - // Inject chunk 2/2 - meta.rcv_tsn = 42; - meta.rcv_cumtsn = 43; - chunk.SetData(" rawr!", 6); - transport1->InjectDataOrNotificationFromSctpForTesting( - chunk.data(), chunk.size(), meta, MSG_EOR); - - // Expect the message to contain both chunks. - EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "meow? rawr!"), kDefaultTimeout); -} - -// Test that data can be sent end-to-end when an SCTP transport starts with one -// transport (which is unwritable), and then switches to another transport. A -// common scenario due to how BUNDLE works. -TEST_F(SctpTransportTest, SwitchDtlsTransport) { - FakeDtlsTransport black_hole("black hole", 0); - FakeDtlsTransport fake_dtls1("fake dtls 1", 0); - FakeDtlsTransport fake_dtls2("fake dtls 2", 0); - SctpFakeDataReceiver recv1; - SctpFakeDataReceiver recv2; - - // Construct transport1 with the "black hole" transport. - std::unique_ptr transport1( - CreateTransport(&black_hole, &recv1)); - std::unique_ptr transport2( - CreateTransport(&fake_dtls2, &recv2)); - - // Add a stream. - transport1->OpenStream(1); - transport2->OpenStream(1); - - // Tell them both to start (though transport1_ is connected to black_hole). - transport1->Start(kTransport1Port, kTransport2Port, kSctpSendBufferSize); - transport2->Start(kTransport2Port, kTransport1Port, kSctpSendBufferSize); - - // Switch transport1_ to the normal fake_dtls1_ transport. - transport1->SetDtlsTransport(&fake_dtls1); - - // Connect the two fake DTLS transports. - bool asymmetric = false; - fake_dtls1.SetDestination(&fake_dtls2, asymmetric); - - // Make sure we end up able to send data. - SendDataResult result; - ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result)); - ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result)); - EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout); - - // Setting a null DtlsTransport should work. This could happen when an SCTP - // data section is rejected. - transport1->SetDtlsTransport(nullptr); -} - -// Calling Start twice shouldn't do anything bad, if with the same parameters. -TEST_F(SctpTransportTest, DuplicateStartCallsIgnored) { - SetupConnectedTransportsWithTwoStreams(); - EXPECT_TRUE(transport1()->Start(kTransport1Port, kTransport2Port, - kSctpSendBufferSize)); - - // Make sure we can still send/recv data. - SendDataResult result; - ASSERT_TRUE(SendData(transport1(), 1, "foo", &result)); - ASSERT_TRUE(SendData(transport2(), 1, "bar", &result)); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "foo"), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 1, "bar"), kDefaultTimeout); -} - -// Calling Start a second time with a different port should fail. -TEST_F(SctpTransportTest, CallingStartWithDifferentPortFails) { - SetupConnectedTransportsWithTwoStreams(); - EXPECT_FALSE(transport1()->Start(kTransport1Port, 1234, kSctpSendBufferSize)); - EXPECT_FALSE(transport1()->Start(1234, kTransport2Port, kSctpSendBufferSize)); -} - -// A value of -1 for the local/remote port should be treated as the default -// (5000). -TEST_F(SctpTransportTest, NegativeOnePortTreatedAsDefault) { - FakeDtlsTransport fake_dtls1("fake dtls 1", 0); - FakeDtlsTransport fake_dtls2("fake dtls 2", 0); - SctpFakeDataReceiver recv1; - SctpFakeDataReceiver recv2; - std::unique_ptr transport1( - CreateTransport(&fake_dtls1, &recv1)); - std::unique_ptr transport2( - CreateTransport(&fake_dtls2, &recv2)); - - // Add a stream. - transport1->OpenStream(1); - transport2->OpenStream(1); - - // Tell them both to start, giving one transport the default port and the - // other transport -1. - transport1->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize); - transport2->Start(-1, -1, kSctpSendBufferSize); - - // Connect the two fake DTLS transports. - bool asymmetric = false; - fake_dtls1.SetDestination(&fake_dtls2, asymmetric); - - // Make sure we end up able to send data. - SendDataResult result; - ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result)); - ASSERT_TRUE(SendData(transport2.get(), 1, "bar", &result)); - EXPECT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "bar"), kDefaultTimeout); -} - -TEST_F(SctpTransportTest, OpenStreamWithAlreadyOpenedStreamFails) { - FakeDtlsTransport fake_dtls("fake dtls", 0); - SctpFakeDataReceiver recv; - std::unique_ptr transport( - CreateTransport(&fake_dtls, &recv)); - EXPECT_TRUE(transport->OpenStream(1)); - EXPECT_FALSE(transport->OpenStream(1)); -} - -TEST_F(SctpTransportTest, ResetStreamWithAlreadyResetStreamFails) { - FakeDtlsTransport fake_dtls("fake dtls", 0); - SctpFakeDataReceiver recv; - std::unique_ptr transport( - CreateTransport(&fake_dtls, &recv)); - EXPECT_TRUE(transport->OpenStream(1)); - EXPECT_TRUE(transport->ResetStream(1)); - EXPECT_FALSE(transport->ResetStream(1)); -} - -// Test that SignalReadyToSendData is fired after Start has been called and the -// DTLS transport is writable. -TEST_F(SctpTransportTest, SignalReadyToSendDataAfterDtlsWritable) { - FakeDtlsTransport fake_dtls("fake dtls", 0); - SctpFakeDataReceiver recv; - std::unique_ptr transport( - CreateTransport(&fake_dtls, &recv)); - SctpTransportObserver observer(transport.get()); - - transport->Start(kSctpDefaultPort, kSctpDefaultPort, kSctpSendBufferSize); - fake_dtls.SetWritable(true); - EXPECT_TRUE_WAIT(observer.ReadyToSend(), kDefaultTimeout); -} - -// Run the below tests using both ordered and unordered mode. -class SctpTransportTestWithOrdered - : public SctpTransportTest, - public ::testing::WithParamInterface {}; - -// Tests that a small message gets buffered and later sent by the -// UsrsctpTransport when the sctp library only accepts the message partially. -TEST_P(SctpTransportTestWithOrdered, SendSmallBufferedOutgoingMessage) { - bool ordered = GetParam(); - SetupConnectedTransportsWithTwoStreams(); - // Wait for initial SCTP association to be formed. - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - // Make the fake transport unwritable so that messages pile up for the SCTP - // socket. - fake_dtls1()->SetWritable(false); - SendDataResult result; - - // Fill almost all of sctp library's send buffer. - ASSERT_TRUE(SendData(transport1(), /*sid=*/1, - std::string(kSctpSendBufferSize - 1, 'a'), &result, - ordered)); - - std::string buffered_message("hello hello"); - // UsrsctpTransport accepts this message by buffering part of it. - ASSERT_TRUE( - SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered)); - ASSERT_TRUE(transport1()->ReadyToSendData()); - - // Sending anything else should block now. - ASSERT_FALSE( - SendData(transport1(), /*sid=*/1, "hello again", &result, ordered)); - ASSERT_EQ(SDR_BLOCK, result); - ASSERT_FALSE(transport1()->ReadyToSendData()); - - // Make sure the ready-to-send count hasn't changed. - EXPECT_EQ(1, transport1_ready_to_send_count()); - // Make the transport writable again and expect a "SignalReadyToSendData" at - // some point after sending the buffered message. - fake_dtls1()->SetWritable(true); - EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message), - kDefaultTimeout); - EXPECT_EQ(2u, receiver2()->num_messages_received()); -} - -// Tests that a large message gets buffered and later sent by the -// UsrsctpTransport when the sctp library only accepts the message partially. -TEST_P(SctpTransportTestWithOrdered, SendLargeBufferedOutgoingMessage) { - bool ordered = GetParam(); - SetupConnectedTransportsWithTwoStreams(); - // Wait for initial SCTP association to be formed. - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - // Make the fake transport unwritable so that messages pile up for the SCTP - // socket. - fake_dtls1()->SetWritable(false); - SendDataResult result; - - // Fill almost all of sctp library's send buffer. - ASSERT_TRUE(SendData(transport1(), /*sid=*/1, - std::string(kSctpSendBufferSize / 2, 'a'), &result, - ordered)); - - std::string buffered_message(kSctpSendBufferSize, 'b'); - // UsrsctpTransport accepts this message by buffering the second half. - ASSERT_TRUE( - SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered)); - ASSERT_TRUE(transport1()->ReadyToSendData()); - - // Sending anything else should block now. - ASSERT_FALSE( - SendData(transport1(), /*sid=*/1, "hello again", &result, ordered)); - ASSERT_EQ(SDR_BLOCK, result); - ASSERT_FALSE(transport1()->ReadyToSendData()); - - // Make sure the ready-to-send count hasn't changed. - EXPECT_EQ(1, transport1_ready_to_send_count()); - // Make the transport writable again and expect a "SignalReadyToSendData" at - // some point. - fake_dtls1()->SetWritable(true); - EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message), - kDefaultTimeout); - EXPECT_EQ(2u, receiver2()->num_messages_received()); -} - -// Tests that a large message gets buffered and later sent by the -// UsrsctpTransport when the sctp library only accepts the message partially -// during a stream reset. -TEST_P(SctpTransportTestWithOrdered, - SendLargeBufferedOutgoingMessageDuringReset) { - bool ordered = GetParam(); - SetupConnectedTransportsWithTwoStreams(); - SctpTransportObserver transport2_observer(transport2()); - - // Wait for initial SCTP association to be formed. - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - // Make the fake transport unwritable so that messages pile up for the SCTP - // socket. - fake_dtls1()->SetWritable(false); - SendDataResult result; - - // Fill almost all of sctp library's send buffer. - ASSERT_TRUE(SendData(transport1(), /*sid=*/1, - std::string(kSctpSendBufferSize / 2, 'a'), &result, - ordered)); - - std::string buffered_message(kSctpSendBufferSize, 'b'); - // UsrsctpTransport accepts this message by buffering the second half. - ASSERT_TRUE( - SendData(transport1(), /*sid=*/1, buffered_message, &result, ordered)); - // Queue a stream reset - transport1()->ResetStream(/*sid=*/1); - - // Make the transport writable again and expect a "SignalReadyToSendData" at - // some point after sending the buffered message. - fake_dtls1()->SetWritable(true); - EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout); - - // Queued message should be received by the receiver before receiving the - // reset - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message), - kDefaultTimeout); - EXPECT_EQ(2u, receiver2()->num_messages_received()); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout); -} - -TEST_P(SctpTransportTestWithOrdered, SendData) { - bool ordered = GetParam(); - SetupConnectedTransportsWithTwoStreams(); - - SendDataResult result; - RTC_LOG(LS_VERBOSE) - << "transport1 sending: 'hello?' -----------------------------"; - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result, ordered)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - RTC_LOG(LS_VERBOSE) << "recv2.received=" << receiver2()->received() - << ", recv2.last_params.sid=" - << receiver2()->last_params().sid - << ", recv2.last_params.seq_num=" - << receiver2()->last_params().seq_num - << ", recv2.last_data=" << receiver2()->last_data(); - - RTC_LOG(LS_VERBOSE) - << "transport2 sending: 'hi transport1' -----------------------------"; - ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result, ordered)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), - kDefaultTimeout); - RTC_LOG(LS_VERBOSE) << "recv1.received=" << receiver1()->received() - << ", recv1.last_params.sid=" - << receiver1()->last_params().sid - << ", recv1.last_params.seq_num=" - << receiver1()->last_params().seq_num - << ", recv1.last_data=" << receiver1()->last_data(); -} - -// Sends a lot of large messages at once and verifies SDR_BLOCK is returned. -TEST_P(SctpTransportTestWithOrdered, SendDataBlocked) { - SetupConnectedTransportsWithTwoStreams(); - - SendDataResult result; - webrtc::SendDataParams params; - params.ordered = GetParam(); - - std::vector buffer(1024 * 64, 0); - - for (size_t i = 0; i < 100; ++i) { - transport1()->SendData( - 1, params, rtc::CopyOnWriteBuffer(&buffer[0], buffer.size()), &result); - if (result == SDR_BLOCK) - break; - } - - EXPECT_EQ(SDR_BLOCK, result); -} - -// Test that after an SCTP socket's buffer is filled, SignalReadyToSendData -// is fired after it begins to be drained. -TEST_P(SctpTransportTestWithOrdered, SignalReadyToSendDataAfterBlocked) { - SetupConnectedTransportsWithTwoStreams(); - // Wait for initial SCTP association to be formed. - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - // Make the fake transport unwritable so that messages pile up for the SCTP - // socket. - fake_dtls1()->SetWritable(false); - // Send messages until we get EWOULDBLOCK. - static const size_t kMaxMessages = 1024; - webrtc::SendDataParams params; - params.ordered = GetParam(); - rtc::CopyOnWriteBuffer buf(1024); - memset(buf.MutableData(), 0, 1024); - SendDataResult result; - size_t message_count = 0; - for (; message_count < kMaxMessages; ++message_count) { - if (!transport1()->SendData(1, params, buf, &result) && - result == SDR_BLOCK) { - break; - } - } - ASSERT_NE(kMaxMessages, message_count) - << "Sent max number of messages without getting SDR_BLOCK?"; - // Make sure the ready-to-send count hasn't changed. - EXPECT_EQ(1, transport1_ready_to_send_count()); - // Make the transport writable again and expect a "SignalReadyToSendData" at - // some point. - fake_dtls1()->SetWritable(true); - EXPECT_EQ_WAIT(2, transport1_ready_to_send_count(), kDefaultTimeout); - EXPECT_EQ_WAIT(message_count, receiver2()->num_messages_received(), - kDefaultTimeout); -} - -INSTANTIATE_TEST_SUITE_P(SctpTransportTest, - SctpTransportTestWithOrdered, - ::testing::Bool()); - -// This is a regression test that fails with earlier versions of SCTP in -// unordered mode. See bugs.webrtc.org/10939. -TEST_F(SctpTransportTest, SendsLargeDataBufferedBySctpLib) { - SetupConnectedTransportsWithTwoStreams(); - // Wait for initial SCTP association to be formed. - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - // Make the fake transport unwritable so that messages pile up for the SCTP - // socket. - fake_dtls1()->SetWritable(false); - - SendDataResult result; - std::string buffered_message(kSctpSendBufferSize - 1, 'a'); - ASSERT_TRUE(SendData(transport1(), 1, buffered_message, &result, false)); - - fake_dtls1()->SetWritable(true); - EXPECT_EQ_WAIT(1, transport1_ready_to_send_count(), kDefaultTimeout); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, buffered_message), - kDefaultTimeout); -} - -// Trying to send data for a nonexistent stream should fail. -TEST_F(SctpTransportTest, SendDataWithNonexistentStreamFails) { - SetupConnectedTransportsWithTwoStreams(); - SendDataResult result; - EXPECT_FALSE(SendData(transport2(), 123, "some data", &result)); - EXPECT_EQ(SDR_ERROR, result); -} - -TEST_F(SctpTransportTest, SendDataHighPorts) { - SetupConnectedTransportsWithTwoStreams(32768, 32769); - - SendDataResult result; - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - - ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), - kDefaultTimeout); -} - -TEST_F(SctpTransportTest, ClosesRemoteStream) { - SetupConnectedTransportsWithTwoStreams(); - SctpTransportObserver transport1_observer(transport1()); - SctpTransportObserver transport2_observer(transport2()); - - SendDataResult result; - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), - kDefaultTimeout); - - // Close stream 1 on transport 1. Transport 2 should notify us. - transport1()->ResetStream(1); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout); -} -TEST_F(SctpTransportTest, ClosesRemoteStreamWithNoData) { - SetupConnectedTransportsWithTwoStreams(); - SctpTransportObserver transport1_observer(transport1()); - SctpTransportObserver transport2_observer(transport2()); - - // Close stream 1 on transport 1. Transport 2 should notify us. - transport1()->ResetStream(1); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout); -} - -TEST_F(SctpTransportTest, ClosesTwoRemoteStreams) { - SetupConnectedTransportsWithTwoStreams(); - AddStream(3); - SctpTransportObserver transport1_observer(transport1()); - SctpTransportObserver transport2_observer(transport2()); - - SendDataResult result; - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), - kDefaultTimeout); - - // Close two streams on one side. - transport2()->ResetStream(2); - transport2()->ResetStream(3); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(2), kDefaultTimeout); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(3), kDefaultTimeout); -} - -TEST_F(SctpTransportTest, ClosesStreamsOnBothSides) { - SetupConnectedTransportsWithTwoStreams(); - AddStream(3); - AddStream(4); - SctpTransportObserver transport1_observer(transport1()); - SctpTransportObserver transport2_observer(transport2()); - - SendDataResult result; - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - ASSERT_TRUE(SendData(transport2(), 2, "hi transport1", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver1(), 2, "hi transport1"), - kDefaultTimeout); - - // Close one stream on transport1(), while closing three streams on - // transport2(). They will conflict (only one side can close anything at a - // time, apparently). Test the resolution of the conflict. - transport1()->ResetStream(1); - - transport2()->ResetStream(2); - transport2()->ResetStream(3); - transport2()->ResetStream(4); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout); - EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(2), kDefaultTimeout); - EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(3), kDefaultTimeout); - EXPECT_TRUE_WAIT(transport1_observer.WasStreamClosed(4), kDefaultTimeout); -} - -TEST_F(SctpTransportTest, RefusesHighNumberedTransports) { - SetupConnectedTransportsWithTwoStreams(); - EXPECT_TRUE(AddStream(kMaxSctpSid)); - EXPECT_FALSE(AddStream(kMaxSctpSid + 1)); -} - -TEST_F(SctpTransportTest, ReusesAStream) { - // Shut down transport 1, then open it up again for reuse. - SetupConnectedTransportsWithTwoStreams(); - SendDataResult result; - SctpTransportObserver transport2_observer(transport2()); - - ASSERT_TRUE(SendData(transport1(), 1, "hello?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hello?"), kDefaultTimeout); - - transport1()->ResetStream(1); - EXPECT_TRUE_WAIT(transport2_observer.WasStreamClosed(1), kDefaultTimeout); - // Transport 1 is gone now. - - // Create a new transport 1. - AddStream(1); - ASSERT_TRUE(SendData(transport1(), 1, "hi?", &result)); - EXPECT_EQ(SDR_SUCCESS, result); - EXPECT_TRUE_WAIT(ReceivedData(receiver2(), 1, "hi?"), kDefaultTimeout); - transport1()->ResetStream(1); - EXPECT_EQ_WAIT(2, transport2_observer.StreamCloseCount(1), kDefaultTimeout); -} - -TEST_F(SctpTransportTest, RejectsTooLargeMessageSize) { - FakeDtlsTransport fake_dtls("fake dtls", 0); - SctpFakeDataReceiver recv; - std::unique_ptr transport( - CreateTransport(&fake_dtls, &recv)); - - EXPECT_FALSE(transport->Start(kSctpDefaultPort, kSctpDefaultPort, - kSctpSendBufferSize + 1)); -} - -TEST_F(SctpTransportTest, RejectsTooSmallMessageSize) { - FakeDtlsTransport fake_dtls("fake dtls", 0); - SctpFakeDataReceiver recv; - std::unique_ptr transport( - CreateTransport(&fake_dtls, &recv)); - - EXPECT_FALSE(transport->Start(kSctpDefaultPort, kSctpDefaultPort, 0)); -} - -TEST_F(SctpTransportTest, RejectsSendTooLargeMessages) { - SetupConnectedTransportsWithTwoStreams(); - // Use "Start" to reduce the max message size - transport1()->Start(kTransport1Port, kTransport2Port, 10); - EXPECT_EQ(10, transport1()->max_message_size()); - const char eleven_characters[] = "12345678901"; - SendDataResult result; - EXPECT_FALSE(SendData(transport1(), 1, eleven_characters, &result)); -} - -// Regression test for: crbug.com/1137936 -TEST_F(SctpTransportTest, SctpRestartWithPendingDataDoesNotDeadlock) { - // In order to trigger a restart, we'll connect two transports, then - // disconnect them and connect the first to a third, which will initiate the - // new handshake. - FakeDtlsTransport fake_dtls1("fake dtls 1", 0); - FakeDtlsTransport fake_dtls2("fake dtls 2", 0); - FakeDtlsTransport fake_dtls3("fake dtls 3", 0); - SctpFakeDataReceiver recv1; - SctpFakeDataReceiver recv2; - SctpFakeDataReceiver recv3; - - std::unique_ptr transport1( - CreateTransport(&fake_dtls1, &recv1)); - std::unique_ptr transport2( - CreateTransport(&fake_dtls2, &recv2)); - std::unique_ptr transport3( - CreateTransport(&fake_dtls3, &recv3)); - SctpTransportObserver observer(transport1.get()); - - // Connect the first two transports. - fake_dtls1.SetDestination(&fake_dtls2, /*asymmetric=*/false); - transport1->OpenStream(1); - transport2->OpenStream(1); - transport1->Start(5000, 5000, kSctpSendBufferSize); - transport2->Start(5000, 5000, kSctpSendBufferSize); - - // Sanity check that we can send data. - SendDataResult result; - ASSERT_TRUE(SendData(transport1.get(), 1, "foo", &result)); - ASSERT_TRUE_WAIT(ReceivedData(&recv2, 1, "foo"), kDefaultTimeout); - - // Disconnect the transports and attempt to send a message, which will be - // stored in an output queue; this is necessary to reproduce the bug. - fake_dtls1.SetDestination(nullptr, /*asymmetric=*/false); - EXPECT_TRUE(SendData(transport1.get(), 1, "bar", &result)); - - // Now connect to the third transport. - fake_dtls1.SetDestination(&fake_dtls3, /*asymmetric=*/false); - transport3->OpenStream(1); - transport3->Start(5000, 5000, kSctpSendBufferSize); - - // Send data from the new endpoint to the original endpoint. If data is - // received that means the restart must have been successful. - EXPECT_TRUE(SendData(transport3.get(), 1, "baz", &result)); - EXPECT_TRUE_WAIT(ReceivedData(&recv1, 1, "baz"), kDefaultTimeout); -} - -} // namespace cricket diff --git a/modules/BUILD.gn b/modules/BUILD.gn index 54dffe0a63..d24bf64cdb 100644 --- a/modules/BUILD.gn +++ b/modules/BUILD.gn @@ -82,12 +82,14 @@ if (rtc_include_tests && !build_with_chromium) { data = modules_tests_resources if (is_android) { + use_default_launcher = false deps += [ # NOTE(brandtr): Including Java classes seems only to be possible from # rtc_test targets. Therefore we include this target here, instead of # in video_coding_modules_tests, where it is actually used. "../sdk/android:libjingle_peerconnection_java", - "//testing/android/native_test:native_test_native_code", + "//sdk/android:native_test_jni_onload", + "//testing/android/native_test:native_test_support", ] shard_timeout = 900 } @@ -231,6 +233,7 @@ if (rtc_include_tests && !build_with_chromium) { data = modules_unittests_resources if (is_android) { + use_default_launcher = false deps += [ "../sdk/android:libjingle_peerconnection_java", "//testing/android/native_test:native_test_support", diff --git a/modules/async_audio_processing/BUILD.gn b/modules/async_audio_processing/BUILD.gn index 9330b67f92..7a7ca20df1 100644 --- a/modules/async_audio_processing/BUILD.gn +++ b/modules/async_audio_processing/BUILD.gn @@ -23,7 +23,7 @@ rtc_library("async_audio_processing") { "../../api/audio:audio_frame_processor", "../../api/task_queue:task_queue", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", "../../rtc_base:rtc_task_queue", ] } @@ -38,7 +38,6 @@ if (rtc_include_tests) { ":async_audio_processing", "../../api/audio:audio_frame_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] } } diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index 0d5e429d42..01de67c246 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -50,8 +50,11 @@ rtc_library("audio_coding") { "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base:audio_format_to_string", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:safe_conversions", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../../system_wrappers:metrics", @@ -70,8 +73,8 @@ rtc_library("legacy_encoded_audio_frame") { deps = [ "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -86,8 +89,8 @@ rtc_library("webrtc_cng") { deps = [ "../../api:array_view", "../../common_audio:common_audio_c", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base:safe_conversions", ] } @@ -118,12 +121,14 @@ rtc_library("red") { deps = [ "../../api:array_view", + "../../api:field_trials_view", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", "../../common_audio", + "../../rtc_base:buffer", + "../../rtc_base:byte_order", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", - "../../system_wrappers:field_trial", + "../../rtc_base:logging", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -143,8 +148,8 @@ rtc_library("g711") { "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] public_deps = [ ":g711_c" ] # no-presubmit-check TODO(webrtc:8603) @@ -175,8 +180,9 @@ rtc_library("g722") { "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs/g722:audio_encoder_g722_config", "../../api/units:time_delta", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] public_deps = [ ":g722_c" ] # no-presubmit-check TODO(webrtc:8603) @@ -208,8 +214,10 @@ rtc_library("ilbc") { "../../api/audio_codecs/ilbc:audio_encoder_ilbc_config", "../../api/units:time_delta", "../../common_audio", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:safe_conversions", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] public_deps = [ ":ilbc_c" ] # no-presubmit-check TODO(webrtc:8603) @@ -364,7 +372,6 @@ rtc_library("ilbc_c") { "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base:sanitizer", "../../rtc_base/system:arch", ] @@ -385,7 +392,6 @@ rtc_source_set("isac_common") { "../../api/audio_codecs:audio_codecs_api", "../../api/units:time_delta", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base:safe_minmax", "../../system_wrappers:field_trial", ] @@ -493,7 +499,6 @@ rtc_library("isac_c") { "../../common_audio:common_audio_c", "../../rtc_base:checks", "../../rtc_base:compile_assert_c", - "../../rtc_base:rtc_base_approved", "../../rtc_base/system:arch", "../third_party/fft", ] @@ -606,7 +611,6 @@ rtc_library("isac_fix_c") { "../../common_audio:common_audio_c", "../../rtc_base:checks", "../../rtc_base:compile_assert_c", - "../../rtc_base:rtc_base_approved", "../../rtc_base:sanitizer", "../../system_wrappers", "../third_party/fft", @@ -680,7 +684,6 @@ if (rtc_build_with_neon) { "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] } } @@ -702,8 +705,8 @@ rtc_library("pcm16b") { ":legacy_encoded_audio_frame", "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] public_deps = [ ":pcm16b_c" ] # no-presubmit-check TODO(webrtc:8603) } @@ -751,11 +754,16 @@ rtc_library("webrtc_opus") { "../../api/audio_codecs:audio_codecs_api", "../../api/audio_codecs/opus:audio_encoder_opus_config", "../../common_audio", + "../../rtc_base:buffer", "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_numerics", + "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../system_wrappers:field_trial", ] absl_deps = [ @@ -790,9 +798,10 @@ rtc_library("webrtc_multiopus") { "../../api/audio_codecs/opus:audio_decoder_opus_config", "../../api/audio_codecs/opus:audio_encoder_opus_config", "../../api/units:time_delta", + "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:logging", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", "../../rtc_base:safe_minmax", "../../rtc_base:stringutils", ] @@ -833,7 +842,6 @@ rtc_library("webrtc_opus_wrapper") { "../../api:array_view", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", - "../../rtc_base:rtc_base_approved", "../../system_wrappers:field_trial", ] } @@ -900,8 +908,10 @@ rtc_library("audio_network_adaptor") { "../../logging:rtc_event_audio", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", + "../../rtc_base:logging", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", + "../../rtc_base:timeutils", "../../rtc_base/system:file_wrapper", "../../system_wrappers", "../../system_wrappers:field_trial", @@ -1006,9 +1016,13 @@ rtc_library("neteq") { "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base:audio_format_to_string", + "../../rtc_base:buffer", "../../rtc_base:checks", + "../../rtc_base:event_tracer", "../../rtc_base:gtest_prod", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", "../../rtc_base:sanitizer", "../../rtc_base/experiments:field_trial_parser", @@ -1071,8 +1085,11 @@ rtc_library("neteq_tools_minimal") { "../../api/neteq:custom_neteq_factory", "../../api/neteq:default_neteq_controller_factory", "../../api/neteq:neteq_api", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:copy_on_write_buffer", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", "../../system_wrappers", "../rtp_rtcp:rtp_rtcp_format", ] @@ -1107,8 +1124,9 @@ rtc_library("neteq_test_tools") { "../../api:rtp_headers", "../../common_audio", "../../rtc_base", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", "../../rtc_base/system:arch", "../../test:rtp_test_utils", "../rtp_rtcp:rtp_rtcp_format", @@ -1149,7 +1167,9 @@ rtc_library("neteq_tools") { "../../api:array_view", "../../api/audio_codecs:audio_codecs_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../rtp_rtcp", "../rtp_rtcp:rtp_rtcp_format", ] @@ -1176,7 +1196,6 @@ rtc_library("neteq_input_audio_tools") { deps = [ "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] } @@ -1193,7 +1212,6 @@ if (rtc_enable_protobuf) { ":neteq_tools_minimal", "../../logging:rtc_event_log_parser", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../rtp_rtcp", "../rtp_rtcp:rtp_rtcp_format", ] @@ -1247,7 +1265,6 @@ rtc_library("audio_coding_modules_tests_shared") { "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", - "../../rtc_base:rtc_base_approved", "../../rtc_base:stringutils", "../../system_wrappers", "../../test:fileutils", @@ -1393,9 +1410,13 @@ if (rtc_include_tests) { "../../api/audio_codecs/opus:audio_encoder_opus", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../test:fileutils", + "../../test:scoped_key_value_config", "../../test:test_support", ] absl_deps = [ @@ -1417,7 +1438,8 @@ if (rtc_include_tests) { ":neteq_test_support", ":neteq_test_tools", "../../api/audio_codecs/opus:audio_encoder_opus", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:timeutils", "../../system_wrappers", "../../system_wrappers:field_trial", "../../test:fileutils", @@ -1435,16 +1457,17 @@ if (rtc_include_tests) { defines = audio_coding_defines - deps = audio_coding_deps + [ - "../../api:scoped_refptr", - ":audio_coding", - "../../api/audio_codecs:audio_codecs_api", - "../../api/audio_codecs:builtin_audio_decoder_factory", - ":neteq_tools", - "../../rtc_base:rtc_base_approved", - "../../test:test_support", - "//testing/gtest", - ] + deps = [ + ":audio_coding", + ":neteq_tools", + "../../api:scoped_refptr", + "../../api/audio_codecs:audio_codecs_api", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../test:test_support", + "//testing/gtest", + ] + + deps += audio_coding_deps absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } @@ -1457,19 +1480,19 @@ if (rtc_include_tests) { defines = audio_coding_defines - deps = audio_coding_deps + [ - "../../api/audio:audio_frame_api", - "../../rtc_base:checks", - ":audio_coding", - ":neteq_tools", - "../../api/audio_codecs:builtin_audio_decoder_factory", - "../../api/audio_codecs:builtin_audio_encoder_factory", - "../../api/audio_codecs:audio_codecs_api", - "../../rtc_base:rtc_base_approved", - "../../test:test_support", - "//testing/gtest", - ] - + deps = [ + ":audio_coding", + ":neteq_tools", + "../../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", + "../../rtc_base:checks", + "../../rtc_base:stringutils", + "../../test:test_support", + "//testing/gtest", + ] + deps += audio_coding_deps absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } @@ -1510,7 +1533,12 @@ if (rtc_include_tests) { data = audio_decoder_unittests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] shard_timeout = 900 } if (is_ios) { @@ -1526,6 +1554,7 @@ if (rtc_include_tests) { defines = audio_codec_defines deps = [ "../../rtc_base:checks", + "../../rtc_base:refcount", "../../test:fileutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -1539,7 +1568,6 @@ if (rtc_include_tests) { ":neteq_test_tools", "../../api/audio_codecs:builtin_audio_decoder_factory", "../../api/neteq:neteq_api", - "../../rtc_base:rtc_base_approved", "../../test:audio_codec_mocks", "../../test:field_trial", "../../test:test_support", @@ -1555,7 +1583,6 @@ if (rtc_include_tests) { deps = [ ":neteq_test_factory", ":neteq_test_tools", - "../../rtc_base:rtc_base_approved", "../../rtc_base:stringutils", "../../system_wrappers:field_trial", "../../test:field_trial", @@ -1586,7 +1613,10 @@ if (rtc_include_tests) { rtc_test("audio_codec_speed_tests") { testonly = true defines = [] - deps = [ "../../test:fileutils" ] + deps = [ + "../../rtc_base:macromagic", + "../../test:fileutils", + ] sources = [ "codecs/isac/fix/test/isac_speed_test.cc", "codecs/opus/opus_speed_test.cc", @@ -1597,7 +1627,12 @@ if (rtc_include_tests) { data = audio_codec_speed_tests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] shard_timeout = 900 } @@ -1609,7 +1644,6 @@ if (rtc_include_tests) { ":isac_fix", ":webrtc_opus", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../test:test_main", "../../test:test_support", "../audio_processing", @@ -1635,7 +1669,6 @@ if (rtc_include_tests) { "../../api/audio_codecs:builtin_audio_decoder_factory", "../../api/neteq:neteq_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../system_wrappers", "../../test:fileutils", "../../test:test_support", @@ -1669,22 +1702,24 @@ if (rtc_include_tests) { rtc_executable("rtp_encode") { testonly = true - 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", - ] + deps = [ + ":audio_coding", + ":audio_encoder_cng", + ":neteq_input_audio_tools", + "../../api/audio:audio_frame_api", + "../../api/audio_codecs/L16:audio_encoder_L16", + "../../api/audio_codecs/g711:audio_encoder_g711", + "../../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", + ] + + deps += audio_coding_deps sources = [ "neteq/tools/rtp_encode.cc" ] @@ -1694,11 +1729,13 @@ if (rtc_include_tests) { rtc_executable("rtp_jitter") { testonly = true - deps = audio_coding_deps + [ - "../rtp_rtcp:rtp_rtcp_format", - "../../api:array_view", - "../../rtc_base:rtc_base_approved", - ] + deps = [ + "../../api:array_view", + "../../rtc_base:buffer", + "../rtp_rtcp:rtp_rtcp_format", + ] + + deps += audio_coding_deps sources = [ "neteq/tools/rtp_jitter.cc" ] @@ -1712,7 +1749,6 @@ if (rtc_include_tests) { deps = [ "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../test:rtp_test_utils", "//testing/gtest", ] @@ -1743,7 +1779,6 @@ if (rtc_include_tests) { ":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", @@ -1776,7 +1811,7 @@ if (rtc_include_tests) { ":neteq_quality_test_support", ":neteq_tools", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", "../../test:fileutils", "../../test:test_main", "//testing/gtest", @@ -1793,7 +1828,6 @@ if (rtc_include_tests) { ":isac_fix", ":neteq", ":neteq_quality_test_support", - "../../rtc_base:rtc_base_approved", "../../test:test_main", "//testing/gtest", "//third_party/abseil-cpp/absl/flags:flag", @@ -1810,7 +1844,7 @@ if (rtc_include_tests) { ":neteq", ":neteq_quality_test_support", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", "../../test:fileutils", "../../test:test_main", "//testing/gtest", @@ -1828,7 +1862,7 @@ if (rtc_include_tests) { ":neteq_quality_test_support", ":pcm16b", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", "../../test:fileutils", "../../test:test_main", "//testing/gtest", @@ -1854,7 +1888,7 @@ if (rtc_include_tests) { deps = [ ":isac", ":isac_test_util", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", ] } } @@ -1884,7 +1918,7 @@ if (rtc_include_tests) { deps = [ ":isac", ":isac_test_util", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", ] } @@ -1918,7 +1952,7 @@ if (rtc_include_tests) { deps = [ ":webrtc_opus", "../../common_audio", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", "../../test:fileutils", "../../test:test_main", "../../test:test_support", @@ -2066,9 +2100,14 @@ if (rtc_include_tests) { "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:refcount", "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:rtc_event", + "../../rtc_base:safe_conversions", "../../rtc_base:sanitizer", + "../../rtc_base:stringutils", "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:arch", @@ -2078,6 +2117,7 @@ if (rtc_include_tests) { "../../test:fileutils", "../../test:rtc_expect_death", "../../test:rtp_test_utils", + "../../test:scoped_key_value_config", "../../test:test_common", "../../test:test_support", "codecs/opus/test", diff --git a/modules/audio_coding/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/acm2/audio_coding_module_unittest.cc index c429cc4723..25a554a453 100644 --- a/modules/audio_coding/acm2/audio_coding_module_unittest.cc +++ b/modules/audio_coding/acm2/audio_coding_module_unittest.cc @@ -895,28 +895,28 @@ class AcmReceiverBitExactnessOldApi : public ::testing::Test { defined(WEBRTC_ARCH_X86_64) TEST_F(AcmReceiverBitExactnessOldApi, 8kHzOutput) { std::string checksum_reference = GetCPUInfo(kAVX2) != 0 - ? "d8671dd38dab43fc9ca64a45c048c218" + ? "f531f3b7dabe96d9e928dece1d3a340b" : "4710c99559aec2f9f02a983ba2146f2d"; Run(/*output_freq_hz=*/8000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 16kHzOutput) { std::string checksum_reference = GetCPUInfo(kAVX2) != 0 - ? "abcb31509af46545edb4f6700728a4de" + ? "c68d7ee520bb35b6d053e017b37bc2b3" : "70b3217df49834b7093c631531068bd0"; Run(/*output_freq_hz=*/16000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 32kHzOutput) { std::string checksum_reference = GetCPUInfo(kAVX2) != 0 - ? "8489b7743d6cd1903807ac81e5ee493d" + ? "dc790e447442ff6105467f29ab7315ae" : "2679e4e596e33259228c62df545eb635"; Run(/*output_freq_hz=*/32000, checksum_reference); } TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutput) { std::string checksum_reference = GetCPUInfo(kAVX2) != 0 - ? "454996a7adb3f62b259a53a09ff624cf" + ? "d118436e154a976009171c4d451d5574" : "f0148c5ef84e74e019ac7057af839102"; Run(/*output_freq_hz=*/48000, checksum_reference); } @@ -996,7 +996,7 @@ TEST_F(AcmReceiverBitExactnessOldApi, 48kHzOutputExternalDecoder) { auto factory = rtc::make_ref_counted(); std::string checksum_reference = GetCPUInfo(kAVX2) != 0 - ? "454996a7adb3f62b259a53a09ff624cf" + ? "d118436e154a976009171c4d451d5574" : "f0148c5ef84e74e019ac7057af839102"; Run(48000, checksum_reference, factory, [](AudioCodingModule* acm) { @@ -1114,8 +1114,7 @@ class AcmSenderBitExactnessOldApi : public ::testing::Test, // Extract and verify the payload checksum. rtc::Buffer checksum_result(payload_checksum_->Size()); payload_checksum_->Finish(checksum_result.data(), checksum_result.size()); - checksum_string = - rtc::hex_encode(checksum_result.data(), checksum_result.size()); + checksum_string = rtc::hex_encode(checksum_result); ExpectChecksumEq(payload_checksum_ref, checksum_string); // Verify number of packets produced. diff --git a/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc b/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc index d580a0509b..46ac671b30 100644 --- a/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc +++ b/modules/audio_coding/codecs/g711/audio_decoder_pcm.cc @@ -40,8 +40,14 @@ int AudioDecoderPcmU::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = + PacketDuration(encoded, encoded_len) * + Channels(); // 1 byte per sample per channel int16_t temp_type = 1; // Default is speech. - size_t ret = WebRtcG711_DecodeU(encoded, encoded_len, decoded, &temp_type); + size_t ret = + WebRtcG711_DecodeU(encoded, encoded_len_adjusted, decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return static_cast(ret); } @@ -75,8 +81,14 @@ int AudioDecoderPcmA::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = + PacketDuration(encoded, encoded_len) * + Channels(); // 1 byte per sample per channel int16_t temp_type = 1; // Default is speech. - size_t ret = WebRtcG711_DecodeA(encoded, encoded_len, decoded, &temp_type); + size_t ret = + WebRtcG711_DecodeA(encoded, encoded_len_adjusted, decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return static_cast(ret); } diff --git a/modules/audio_coding/codecs/g722/audio_decoder_g722.cc b/modules/audio_coding/codecs/g722/audio_decoder_g722.cc index f02ca7f896..1ecc9bc3d1 100644 --- a/modules/audio_coding/codecs/g722/audio_decoder_g722.cc +++ b/modules/audio_coding/codecs/g722/audio_decoder_g722.cc @@ -89,16 +89,22 @@ int AudioDecoderG722StereoImpl::DecodeInternal(const uint8_t* encoded, int16_t* decoded, SpeechType* speech_type) { RTC_DCHECK_EQ(SampleRateHz(), sample_rate_hz); + // Adjust the encoded length down to ensure the same number of samples in each + // channel. + const size_t encoded_len_adjusted = PacketDuration(encoded, encoded_len) * + Channels() / + 2; // 1/2 byte per sample per channel int16_t temp_type = 1; // Default is speech. // De-interleave the bit-stream into two separate payloads. - uint8_t* encoded_deinterleaved = new uint8_t[encoded_len]; - SplitStereoPacket(encoded, encoded_len, encoded_deinterleaved); + uint8_t* encoded_deinterleaved = new uint8_t[encoded_len_adjusted]; + SplitStereoPacket(encoded, encoded_len_adjusted, encoded_deinterleaved); // Decode left and right. - size_t decoded_len = WebRtcG722_Decode(dec_state_left_, encoded_deinterleaved, - encoded_len / 2, decoded, &temp_type); + size_t decoded_len = + WebRtcG722_Decode(dec_state_left_, encoded_deinterleaved, + encoded_len_adjusted / 2, decoded, &temp_type); size_t ret = WebRtcG722_Decode( - dec_state_right_, &encoded_deinterleaved[encoded_len / 2], - encoded_len / 2, &decoded[decoded_len], &temp_type); + dec_state_right_, &encoded_deinterleaved[encoded_len_adjusted / 2], + encoded_len_adjusted / 2, &decoded[decoded_len], &temp_type); if (ret == decoded_len) { ret += decoded_len; // Return total number of samples. // Interleave output. @@ -114,6 +120,14 @@ int AudioDecoderG722StereoImpl::DecodeInternal(const uint8_t* encoded, return static_cast(ret); } +int AudioDecoderG722StereoImpl::PacketDuration(const uint8_t* encoded, + size_t encoded_len) const { + // 1/2 encoded byte per sample per channel. Make sure the length represents + // an equal number of bytes per channel. Otherwise, we cannot de-interleave + // the encoded data later. + return static_cast(2 * (encoded_len / Channels())); +} + int AudioDecoderG722StereoImpl::SampleRateHz() const { return 16000; } diff --git a/modules/audio_coding/codecs/g722/audio_decoder_g722.h b/modules/audio_coding/codecs/g722/audio_decoder_g722.h index 39e9e630be..5872fad5de 100644 --- a/modules/audio_coding/codecs/g722/audio_decoder_g722.h +++ b/modules/audio_coding/codecs/g722/audio_decoder_g722.h @@ -57,6 +57,7 @@ class AudioDecoderG722StereoImpl final : public AudioDecoder { std::vector ParsePayload(rtc::Buffer&& payload, uint32_t timestamp) override; int SampleRateHz() const override; + int PacketDuration(const uint8_t* encoded, size_t encoded_len) const override; size_t Channels() const override; protected: diff --git a/modules/audio_coding/codecs/ilbc/test/iLBC_test.c b/modules/audio_coding/codecs/ilbc/test/iLBC_test.c index 4dbc18513a..e0ca075eda 100644 --- a/modules/audio_coding/codecs/ilbc/test/iLBC_test.c +++ b/modules/audio_coding/codecs/ilbc/test/iLBC_test.c @@ -50,7 +50,6 @@ int main(int argc, char* argv[]) int len_int, mode; short pli; int blockcount = 0; - int packetlosscount = 0; size_t frameLen, len, len_i16s; int16_t speechType; IlbcEncoderInstance *Enc_Inst; @@ -189,7 +188,6 @@ int main(int argc, char* argv[]) /* Packet loss -> remove info from frame */ memset(encoded_data, 0, sizeof(int16_t)*ILBCNOOFWORDS_MAX); - packetlosscount++; } } else { fprintf(stderr, "Error. Channel file too short\n"); diff --git a/modules/audio_coding/codecs/isac/fix/include/isacfix.h b/modules/audio_coding/codecs/isac/fix/include/isacfix.h index 87956a6997..dcc7b0991d 100644 --- a/modules/audio_coding/codecs/isac/fix/include/isacfix.h +++ b/modules/audio_coding/codecs/isac/fix/include/isacfix.h @@ -394,7 +394,7 @@ int16_t WebRtcIsacfix_FreeInternal(ISACFIX_MainStruct* ISAC_main_inst); /**************************************************************************** * WebRtcIsacfix_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. It should always return a complete packet, i.e. only called once * even for 60 msec frames * diff --git a/modules/audio_coding/codecs/isac/fix/source/isacfix.c b/modules/audio_coding/codecs/isac/fix/source/isacfix.c index 9a66591de1..a7d44e883d 100644 --- a/modules/audio_coding/codecs/isac/fix/source/isacfix.c +++ b/modules/audio_coding/codecs/isac/fix/source/isacfix.c @@ -381,7 +381,7 @@ int WebRtcIsacfix_Encode(ISACFIX_MainStruct *ISAC_main_inst, /**************************************************************************** * WebRtcIsacfix_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. It should always return a complete packet, i.e. only called once * even for 60 msec frames * diff --git a/modules/audio_coding/codecs/isac/main/include/isac.h b/modules/audio_coding/codecs/isac/main/include/isac.h index f45bbb3897..3b05a8bcda 100644 --- a/modules/audio_coding/codecs/isac/main/include/isac.h +++ b/modules/audio_coding/codecs/isac/main/include/isac.h @@ -453,7 +453,7 @@ int16_t WebRtcIsac_SetEncSampRate(ISACStruct* ISAC_main_inst, /****************************************************************************** * WebRtcIsac_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. If the rate is set to a value less than bottleneck of codec * the new bistream will be re-encoded with the given target rate. * It should always return a complete packet, i.e. only called once diff --git a/modules/audio_coding/codecs/isac/main/source/isac.c b/modules/audio_coding/codecs/isac/main/source/isac.c index 73f132c228..456f447d9a 100644 --- a/modules/audio_coding/codecs/isac/main/source/isac.c +++ b/modules/audio_coding/codecs/isac/main/source/isac.c @@ -678,7 +678,7 @@ int WebRtcIsac_Encode(ISACStruct* ISAC_main_inst, /****************************************************************************** * WebRtcIsac_GetNewBitStream(...) * - * This function returns encoded data, with the recieved bwe-index in the + * This function returns encoded data, with the received bwe-index in the * stream. If the rate is set to a value less than bottleneck of codec * the new bistream will be re-encoded with the given target rate. * It should always return a complete packet, i.e. only called once diff --git a/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc b/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc index 9129df8fb8..57ebb54f44 100644 --- a/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc +++ b/modules/audio_coding/codecs/isac/main/test/ReleaseTest-API/ReleaseTest-API.cc @@ -22,7 +22,6 @@ /* include API */ #include "modules/audio_coding/codecs/isac/main/include/isac.h" #include "modules/audio_coding/codecs/isac/main/util/utility.h" -#include "rtc_base/format_macros.h" /* Defines */ #define SEED_FILE \ @@ -887,7 +886,7 @@ int main(int argc, char* argv[]) { #endif } printf("\n"); - printf("total bits = %" RTC_PRIuS " bits\n", totalbits); + printf("total bits = %zu bits\n", totalbits); printf("measured average bitrate = %0.3f kbits/s\n", (double)totalbits * (sampFreqKHz) / totalsmpls); if (doTransCoding) { @@ -906,14 +905,16 @@ int main(int argc, char* argv[]) { (100 * runtime / length_file)); if (maxStreamLen30 != 0) { - printf("Maximum payload size 30ms Frames %" RTC_PRIuS - " bytes (%0.3f kbps)\n", - maxStreamLen30, maxStreamLen30 * 8 / 30.); + printf( + "Maximum payload size 30ms Frames %zu" + " bytes (%0.3f kbps)\n", + maxStreamLen30, maxStreamLen30 * 8 / 30.); } if (maxStreamLen60 != 0) { - printf("Maximum payload size 60ms Frames %" RTC_PRIuS - " bytes (%0.3f kbps)\n", - maxStreamLen60, maxStreamLen60 * 8 / 60.); + printf( + "Maximum payload size 60ms Frames %zu" + " bytes (%0.3f kbps)\n", + maxStreamLen60, maxStreamLen60 * 8 / 60.); } // fprintf(stderr, "\n"); @@ -921,12 +922,12 @@ int main(int argc, char* argv[]) { fprintf(stderr, " %0.1f kbps", (double)totalbits * (sampFreqKHz) / totalsmpls); if (maxStreamLen30 != 0) { - fprintf(stderr, " plmax-30ms %" RTC_PRIuS " bytes (%0.0f kbps)", - maxStreamLen30, maxStreamLen30 * 8 / 30.); + fprintf(stderr, " plmax-30ms %zu bytes (%0.0f kbps)", maxStreamLen30, + maxStreamLen30 * 8 / 30.); } if (maxStreamLen60 != 0) { - fprintf(stderr, " plmax-60ms %" RTC_PRIuS " bytes (%0.0f kbps)", - maxStreamLen60, maxStreamLen60 * 8 / 60.); + fprintf(stderr, " plmax-60ms %zu bytes (%0.0f kbps)", maxStreamLen60, + maxStreamLen60 * 8 / 60.); } if (doTransCoding) { fprintf(stderr, " transcoding rate %.0f kbps", diff --git a/modules/audio_coding/codecs/isac/main/test/simpleKenny.c b/modules/audio_coding/codecs/isac/main/test/simpleKenny.c index 116b051ed2..96b9b23004 100644 --- a/modules/audio_coding/codecs/isac/main/test/simpleKenny.c +++ b/modules/audio_coding/codecs/isac/main/test/simpleKenny.c @@ -28,7 +28,6 @@ /* include API */ #include "modules/audio_coding/codecs/isac/main/include/isac.h" #include "modules/audio_coding/codecs/isac/main/util/utility.h" -#include "rtc_base/format_macros.h" /* max number of samples per frame (= 60 ms frame) */ #define MAX_FRAMESAMPLES_SWB 1920 @@ -420,7 +419,7 @@ int main(int argc, char* argv[]) { printf("\n"); printf("Measured bit-rate........... %0.3f kbps\n", rate); printf("Measured RCU bit-ratre...... %0.3f kbps\n", rateRCU); - printf("Maximum bit-rate/payloadsize %0.3f / %" RTC_PRIuS "\n", + printf("Maximum bit-rate/payloadsize %0.3f / %zu\n", maxStreamLen * 8 / 0.03, maxStreamLen); printf("Measured packet-loss........ %0.1f%% \n", 100.0f * (float)lostPacketCntr / (float)packetCntr); diff --git a/modules/audio_coding/codecs/opus/opus_complexity_unittest.cc b/modules/audio_coding/codecs/opus/opus_complexity_unittest.cc index dbb808c161..6388f33303 100644 --- a/modules/audio_coding/codecs/opus/opus_complexity_unittest.cc +++ b/modules/audio_coding/codecs/opus/opus_complexity_unittest.cc @@ -10,7 +10,6 @@ #include "api/audio_codecs/opus/audio_encoder_opus.h" #include "modules/audio_coding/neteq/tools/audio_loop.h" -#include "rtc_base/format_macros.h" #include "rtc_base/time_utils.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" diff --git a/modules/audio_coding/codecs/opus/opus_fec_test.cc b/modules/audio_coding/codecs/opus/opus_fec_test.cc index 0636935b6b..815f26e31c 100644 --- a/modules/audio_coding/codecs/opus/opus_fec_test.cc +++ b/modules/audio_coding/codecs/opus/opus_fec_test.cc @@ -11,7 +11,6 @@ #include #include "modules/audio_coding/codecs/opus/opus_interface.h" -#include "rtc_base/format_macros.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -70,8 +69,7 @@ class OpusFecTest : public TestWithParam { void OpusFecTest::SetUp() { channels_ = get<0>(GetParam()); bit_rate_ = get<1>(GetParam()); - printf("Coding %" RTC_PRIuS " channel signal at %d bps.\n", channels_, - bit_rate_); + printf("Coding %zu channel signal at %d bps.\n", channels_, bit_rate_); in_filename_ = test::ResourcePath(get<2>(GetParam()), get<3>(GetParam())); diff --git a/modules/audio_coding/codecs/opus/test/BUILD.gn b/modules/audio_coding/codecs/opus/test/BUILD.gn index 32eb6ad195..8bc0bf5e0e 100644 --- a/modules/audio_coding/codecs/opus/test/BUILD.gn +++ b/modules/audio_coding/codecs/opus/test/BUILD.gn @@ -47,7 +47,7 @@ if (rtc_include_tests) { ":test", "../../../../../common_audio", "../../../../../common_audio:common_audio_c", - "../../../../../rtc_base:rtc_base_approved", + "../../../../../rtc_base:macromagic", "../../../../../test:test_support", "//testing/gtest", ] diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc index 9643c7b1a5..a9208debdf 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc @@ -18,7 +18,6 @@ #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { static constexpr const int kRedMaxPacketSize = @@ -40,9 +39,9 @@ AudioEncoderCopyRed::Config::Config() = default; AudioEncoderCopyRed::Config::Config(Config&&) = default; AudioEncoderCopyRed::Config::~Config() = default; -size_t GetMaxRedundancyFromFieldTrial() { +size_t GetMaxRedundancyFromFieldTrial(const FieldTrialsView& field_trials) { const std::string red_trial = - webrtc::field_trial::FindFullName("WebRTC-Audio-Red-For-Opus"); + field_trials.Lookup("WebRTC-Audio-Red-For-Opus"); size_t redundancy = 0; if (sscanf(red_trial.c_str(), "Enabled-%zu", &redundancy) != 1 || redundancy > 9) { @@ -51,14 +50,16 @@ size_t GetMaxRedundancyFromFieldTrial() { return redundancy; } -AudioEncoderCopyRed::AudioEncoderCopyRed(Config&& config) +AudioEncoderCopyRed::AudioEncoderCopyRed(Config&& config, + const FieldTrialsView& field_trials) : speech_encoder_(std::move(config.speech_encoder)), primary_encoded_(0, kAudioMaxRtpPacketLen), max_packet_length_(kAudioMaxRtpPacketLen), red_payload_type_(config.payload_type) { RTC_CHECK(speech_encoder_) << "Speech encoder not provided."; - auto number_of_redundant_encodings = GetMaxRedundancyFromFieldTrial(); + auto number_of_redundant_encodings = + GetMaxRedundancyFromFieldTrial(field_trials); for (size_t i = 0; i < number_of_redundant_encodings; i++) { std::pair redundant; redundant.second.EnsureCapacity(kAudioMaxRtpPacketLen); diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h index d163193251..359b5eaa17 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.h +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.h @@ -21,6 +21,7 @@ #include "absl/types/optional.h" #include "api/array_view.h" #include "api/audio_codecs/audio_encoder.h" +#include "api/field_trials_view.h" #include "api/units/time_delta.h" #include "rtc_base/buffer.h" @@ -42,7 +43,7 @@ class AudioEncoderCopyRed final : public AudioEncoder { std::unique_ptr speech_encoder; }; - explicit AudioEncoderCopyRed(Config&& config); + AudioEncoderCopyRed(Config&& config, const FieldTrialsView& field_trials); ~AudioEncoderCopyRed() override; diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc index 0eeac011e0..795a996624 100644 --- a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc +++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc @@ -18,6 +18,7 @@ #include "test/field_trial.h" #include "test/gtest.h" #include "test/mock_audio_encoder.h" +#include "test/scoped_key_value_config.h" #include "test/testsupport/rtc_expect_death.h" using ::testing::_; @@ -49,7 +50,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::unique_ptr(mock_encoder_); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials_)); memset(audio_, 0, sizeof(audio_)); EXPECT_CALL(*mock_encoder_, NumChannels()).WillRepeatedly(Return(1U)); EXPECT_CALL(*mock_encoder_, SampleRateHz()) @@ -68,6 +69,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { timestamp_ += rtc::checked_cast(num_audio_samples_10ms); } + test::ScopedKeyValueConfig field_trials_; MockAudioEncoder* mock_encoder_; std::unique_ptr red_; uint32_t timestamp_; @@ -198,13 +200,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes1) { // Checks that the correct payload sizes are populated into the redundancy // information for a redundancy level of 0. TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes0) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-0/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-0/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence // of calls. @@ -224,13 +226,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes0) { // Checks that the correct payload sizes are populated into the redundancy // information for a redundancy level of 2. TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-2/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-2/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence // of calls. @@ -266,13 +268,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) { // Checks that the correct payload sizes are populated into the redundancy // information for a redundancy level of 3. TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes3) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-3/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-3/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials_)); // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence // of calls. @@ -463,13 +465,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header) { // Variant with a redundancy of 0. TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header0) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-0/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-0/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); const int primary_payload_type = red_payload_type_ + 1; AudioEncoder::EncodedInfo info; @@ -491,13 +493,13 @@ TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header0) { } // Variant with a redundancy of 2. TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header2) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Audio-Red-For-Opus/Enabled-2/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-Audio-Red-For-Opus/Enabled-2/"); // Recreate the RED encoder to take the new field trial setting into account. AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type_; config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]); - red_.reset(new AudioEncoderCopyRed(std::move(config))); + red_.reset(new AudioEncoderCopyRed(std::move(config), field_trials)); const int primary_payload_type = red_payload_type_ + 1; AudioEncoder::EncodedInfo info; @@ -623,11 +625,13 @@ TEST_F(AudioEncoderCopyRedDeathTest, WrongFrameSize) { } TEST_F(AudioEncoderCopyRedDeathTest, NullSpeechEncoder) { + test::ScopedKeyValueConfig field_trials; AudioEncoderCopyRed* red = NULL; AudioEncoderCopyRed::Config config; config.speech_encoder = NULL; - RTC_EXPECT_DEATH(red = new AudioEncoderCopyRed(std::move(config)), - "Speech encoder not provided."); + RTC_EXPECT_DEATH( + red = new AudioEncoderCopyRed(std::move(config), field_trials), + "Speech encoder not provided."); // The delete operation is needed to avoid leak reports from memcheck. delete red; } diff --git a/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc b/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc index f61aacc474..537e6fcede 100644 --- a/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc +++ b/modules/audio_coding/codecs/tools/audio_codec_speed_test.cc @@ -11,7 +11,6 @@ #include "modules/audio_coding/codecs/tools/audio_codec_speed_test.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -100,7 +99,7 @@ void AudioCodecSpeedTest::EncodeDecode(size_t audio_duration_sec) { size_t time_now_ms = 0; float time_ms; - printf("Coding %d kHz-sampled %" RTC_PRIuS "-channel audio at %d bps ...\n", + printf("Coding %d kHz-sampled %zu-channel audio at %d bps ...\n", input_sampling_khz_, channels_, bit_rate_); while (time_now_ms < audio_duration_sec * 1000) { diff --git a/modules/audio_coding/neteq/decision_logic.cc b/modules/audio_coding/neteq/decision_logic.cc index 30463fcc49..f81535c84d 100644 --- a/modules/audio_coding/neteq/decision_logic.cc +++ b/modules/audio_coding/neteq/decision_logic.cc @@ -39,6 +39,10 @@ std::unique_ptr CreateDelayManager( return std::make_unique(config, neteq_config.tick_timer); } +bool IsExpand(NetEq::Mode mode) { + return mode == NetEq::Mode::kExpand || mode == NetEq::Mode::kCodecPlc; +} + } // namespace DecisionLogic::DecisionLogic(NetEqController::Config config) @@ -56,21 +60,14 @@ DecisionLogic::DecisionLogic( disallow_time_stretching_(!config.allow_time_stretching), timescale_countdown_( tick_timer_->GetNewCountdown(kMinTimescaleInterval + 1)), - estimate_dtx_delay_("estimate_dtx_delay", true), - time_stretch_cn_("time_stretch_cn", true), target_level_window_ms_("target_level_window", kDefaultTargetLevelWindowMs, 0, absl::nullopt) { const std::string field_trial_name = field_trial::FindFullName("WebRTC-Audio-NetEqDecisionLogicSettings"); - ParseFieldTrial( - {&estimate_dtx_delay_, &time_stretch_cn_, &target_level_window_ms_}, - field_trial_name); + ParseFieldTrial({&target_level_window_ms_}, field_trial_name); RTC_LOG(LS_INFO) << "NetEq decision logic settings:" - " estimate_dtx_delay=" - << estimate_dtx_delay_ - << " time_stretch_cn=" << time_stretch_cn_ << " target_level_window_ms=" << target_level_window_ms_; } @@ -119,9 +116,12 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, cng_state_ = kCngInternalOn; } - size_t cur_size_samples = estimate_dtx_delay_ - ? status.packet_buffer_info.span_samples - : status.packet_buffer_info.num_samples; + if (IsExpand(status.last_mode)) { + ++num_consecutive_expands_; + } else { + num_consecutive_expands_ = 0; + } + prev_time_scale_ = prev_time_scale_ && (status.last_mode == NetEq::Mode::kAccelerateSuccess || @@ -132,10 +132,8 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, // Do not update buffer history if currently playing CNG since it will bias // the filtered buffer level. if (status.last_mode != NetEq::Mode::kRfc3389Cng && - status.last_mode != NetEq::Mode::kCodecInternalCng && - !(status.next_packet && status.next_packet->is_dtx && - !estimate_dtx_delay_)) { - FilterBufferLevel(cur_size_samples); + status.last_mode != NetEq::Mode::kCodecInternalCng) { + FilterBufferLevel(status.packet_buffer_info.span_samples); } // Guard for errors, to avoid getting stuck in error mode. @@ -173,16 +171,12 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, // if the mute factor is low enough (otherwise the expansion was short enough // to not be noticable). // Note that the MuteFactor is in Q14, so a value of 16384 corresponds to 1. - const size_t current_span = - estimate_dtx_delay_ ? status.packet_buffer_info.span_samples - : status.packet_buffer_info.span_samples_no_dtx; const int target_level_samples = delay_manager_->TargetDelayMs() * sample_rate_ / 1000; - if ((status.last_mode == NetEq::Mode::kExpand || - status.last_mode == NetEq::Mode::kCodecPlc) && - status.expand_mutefactor < 16384 / 2 && - current_span < static_cast(target_level_samples * - kPostponeDecodingLevel / 100) && + if (IsExpand(status.last_mode) && status.expand_mutefactor < 16384 / 2 && + status.packet_buffer_info.span_samples < + static_cast(target_level_samples * kPostponeDecodingLevel / + 100) && !status.packet_buffer_info.dtx_or_cng) { return NetEq::Operation::kExpand; } @@ -206,12 +200,8 @@ NetEq::Operation DecisionLogic::GetDecision(const NetEqStatus& status, } } -void DecisionLogic::ExpandDecision(NetEq::Operation operation) { - if (operation == NetEq::Operation::kExpand) { - num_consecutive_expands_++; - } else { - num_consecutive_expands_ = 0; - } +void DecisionLogic::NotifyMutedState() { + ++num_consecutive_expands_; } absl::optional DecisionLogic::PacketArrived( @@ -348,10 +338,9 @@ NetEq::Operation DecisionLogic::FuturePacketAvailable( // Check if we should continue with an ongoing expand because the new packet // is too far into the future. uint32_t timestamp_leap = available_timestamp - target_timestamp; - if ((prev_mode == NetEq::Mode::kExpand || - prev_mode == NetEq::Mode::kCodecPlc) && - !ReinitAfterExpands(timestamp_leap) && !MaxWaitForPacket() && - PacketTooEarly(timestamp_leap) && UnderTargetLevel()) { + if (IsExpand(prev_mode) && !ReinitAfterExpands(timestamp_leap) && + !MaxWaitForPacket() && PacketTooEarly(timestamp_leap) && + UnderTargetLevel()) { if (play_dtmf) { // Still have DTMF to play, so do not do expand. return NetEq::Operation::kDtmf; @@ -368,40 +357,26 @@ NetEq::Operation DecisionLogic::FuturePacketAvailable( // If previous was comfort noise, then no merge is needed. if (prev_mode == NetEq::Mode::kRfc3389Cng || prev_mode == NetEq::Mode::kCodecInternalCng) { - size_t cur_size_samples = - estimate_dtx_delay_ - ? span_samples_in_packet_buffer - : num_packets_in_packet_buffer * decoder_frame_length; - // Target level is in number of packets in Q8. const size_t target_level_samples = delay_manager_->TargetDelayMs() * sample_rate_ / 1000; const bool generated_enough_noise = static_cast(generated_noise_samples + target_timestamp) >= available_timestamp; - - if (time_stretch_cn_) { - const size_t target_threshold_samples = - target_level_window_ms_ / 2 * (sample_rate_ / 1000); - const bool above_target_window = - cur_size_samples > target_level_samples + target_threshold_samples; - const bool below_target_window = - target_level_samples > target_threshold_samples && - cur_size_samples < target_level_samples - target_threshold_samples; - // Keep the delay same as before CNG, but make sure that it is within the - // target window. - if ((generated_enough_noise && !below_target_window) || - above_target_window) { - time_stretched_cn_samples_ = timestamp_leap - generated_noise_samples; - return NetEq::Operation::kNormal; - } - } else { - // Keep the same delay as before the CNG, but make sure that the number of - // samples in buffer is no higher than 4 times the optimal level. - if (generated_enough_noise || - cur_size_samples > target_level_samples * 4) { - // Time to play this new packet. - return NetEq::Operation::kNormal; - } + const size_t target_threshold_samples = + target_level_window_ms_ / 2 * (sample_rate_ / 1000); + const bool above_target_window = + span_samples_in_packet_buffer > + target_level_samples + target_threshold_samples; + const bool below_target_window = + target_level_samples > target_threshold_samples && + span_samples_in_packet_buffer < + target_level_samples - target_threshold_samples; + // Keep the delay same as before CNG, but make sure that it is within the + // target window. + if ((generated_enough_noise && !below_target_window) || + above_target_window) { + time_stretched_cn_samples_ = timestamp_leap - generated_noise_samples; + return NetEq::Operation::kNormal; } // Too early to play this new packet; keep on playing comfort noise. diff --git a/modules/audio_coding/neteq/decision_logic.h b/modules/audio_coding/neteq/decision_logic.h index a8571ade96..22fb9f7748 100644 --- a/modules/audio_coding/neteq/decision_logic.h +++ b/modules/audio_coding/neteq/decision_logic.h @@ -68,11 +68,7 @@ class DecisionLogic : public NetEqController { // Resets the `cng_state_` to kCngOff. void SetCngOff() override { cng_state_ = kCngOff; } - // Reports back to DecisionLogic whether the decision to do expand remains or - // not. Note that this is necessary, since an expand decision can be changed - // to kNormal in NetEqImpl::GetDecision if there is still enough data in the - // sync buffer. - void ExpandDecision(NetEq::Operation operation) override; + void ExpandDecision(NetEq::Operation operation) override {} // Adds `value` to `sample_memory_`. void AddSampleMemory(int32_t value) override { sample_memory_ += value; } @@ -85,7 +81,7 @@ class DecisionLogic : public NetEqController { void RegisterEmptyPacket() override {} - void NotifyMutedState() override {} + void NotifyMutedState() override; bool SetMaximumDelay(int delay_ms) override { return delay_manager_->SetMaximumDelay(delay_ms); @@ -191,8 +187,6 @@ class DecisionLogic : public NetEqController { int time_stretched_cn_samples_ = 0; bool last_pack_cng_or_dtmf_ = true; bool buffer_flush_ = false; - FieldTrialParameter estimate_dtx_delay_; - FieldTrialParameter time_stretch_cn_; FieldTrialConstrained target_level_window_ms_; }; diff --git a/modules/audio_coding/neteq/decision_logic_unittest.cc b/modules/audio_coding/neteq/decision_logic_unittest.cc index b82165389f..d70e3070f3 100644 --- a/modules/audio_coding/neteq/decision_logic_unittest.cc +++ b/modules/audio_coding/neteq/decision_logic_unittest.cc @@ -18,7 +18,6 @@ #include "modules/audio_coding/neteq/delay_manager.h" #include "modules/audio_coding/neteq/mock/mock_buffer_level_filter.h" #include "modules/audio_coding/neteq/mock/mock_delay_manager.h" -#include "test/field_trial.h" #include "test/gtest.h" namespace webrtc { @@ -54,10 +53,6 @@ using ::testing::Return; class DecisionLogicTest : public ::testing::Test { protected: DecisionLogicTest() { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-NetEqDecisionLogicSettings/" - "estimate_dtx_delay:true,time_stretch_cn:true/"); - NetEqController::Config config; config.tick_timer = &tick_timer_; config.allow_time_stretching = true; diff --git a/modules/audio_coding/neteq/nack_tracker.cc b/modules/audio_coding/neteq/nack_tracker.cc index 35afb736c8..04cc5b52e8 100644 --- a/modules/audio_coding/neteq/nack_tracker.cc +++ b/modules/audio_coding/neteq/nack_tracker.cc @@ -225,8 +225,12 @@ int64_t NackTracker::TimeToPlay(uint32_t timestamp) const { std::vector NackTracker::GetNackList(int64_t round_trip_time_ms) { RTC_DCHECK_GE(round_trip_time_ms, 0); std::vector sequence_numbers; - if (config_.require_valid_rtt && round_trip_time_ms == 0) { - return sequence_numbers; + if (round_trip_time_ms == 0) { + if (config_.require_valid_rtt) { + return sequence_numbers; + } else { + round_trip_time_ms = config_.default_rtt_ms; + } } if (packet_loss_rate_ > static_cast(config_.max_loss_rate * (1 << 30))) { diff --git a/modules/audio_coding/neteq/nack_tracker.h b/modules/audio_coding/neteq/nack_tracker.h index 0cc95b0882..14ba2166d1 100644 --- a/modules/audio_coding/neteq/nack_tracker.h +++ b/modules/audio_coding/neteq/nack_tracker.h @@ -111,6 +111,8 @@ class NackTracker { bool never_nack_multiple_times = false; // Only nack if the RTT is valid. bool require_valid_rtt = false; + // Default RTT to use unless `require_valid_rtt` is set. + int default_rtt_ms = 100; // Do not nack if the loss rate is above this value. double max_loss_rate = 1.0; }; diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index 30886c3ace..d77acc04dd 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -1455,6 +1455,7 @@ int NetEqImpl::DecodeCng(AudioDecoder* decoder, return kDecodedTooMuch; } } + stats_->GeneratedNoiseSamples(*decoded_length); return 0; } diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc index b39a880292..6e1be42a83 100644 --- a/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -320,7 +320,7 @@ TEST_F(NetEqImplTest, InsertPacket) { *dec = std::move(mock_decoder); })); DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, mock_decoder_factory); + absl::nullopt, mock_decoder_factory.get()); // Expectations for decoder database. EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType)) @@ -1367,13 +1367,6 @@ TEST_F(NetEqImplTest, DecodingError) { // We are not expecting anything for output.speech_type_, since an error was // returned. - // Pull audio again, should continue an expansion. - EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); - EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_); - EXPECT_EQ(1u, output.num_channels_); - EXPECT_EQ(AudioFrame::kPLC, output.speech_type_); - EXPECT_THAT(output.packet_infos_, IsEmpty()); - // Pull audio again, should behave normal. EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted)); EXPECT_EQ(kMaxOutputSize, output.samples_per_channel_); @@ -1640,7 +1633,7 @@ TEST_F(NetEqImplTest, NoCrashWith1000Channels) { decoder = dec->get(); })); DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, mock_decoder_factory); + absl::nullopt, mock_decoder_factory.get()); // Expectations for decoder database. EXPECT_CALL(*mock_decoder_database_, GetDecoderInfo(kPayloadType)) .WillRepeatedly(Return(&info)); diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc index 4ff1a431e1..f387b15be6 100644 --- a/modules/audio_coding/neteq/neteq_unittest.cc +++ b/modules/audio_coding/neteq/neteq_unittest.cc @@ -57,10 +57,10 @@ TEST_F(NetEqDecodingTest, MAYBE_TestBitExactness) { webrtc::test::ResourcePath("audio_coding/neteq_universal_new", "rtp"); const std::string output_checksum = - "ba4fae83a52f5e9d95b0910f05d540114285697b"; + "5e56fabfacd6fa202f3a00bcb4e034d6d817e6b3"; const std::string network_stats_checksum = - "fa878a8464ef1cb3d01503b7f927c3e2ce6f02c4"; + "dfbf60f913a25a1f2f1066f85b4b08c24eed0ef2"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); @@ -79,11 +79,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusBitExactness) { // The checksum depends on SSE being enabled, the second part is the non-SSE // checksum. const std::string output_checksum = - "6e23d8827ae54ca352e1448ae363bdfd2878c78e|" - "47cddbf3494b0233f48cb350676e704807237545"; + "919e5eb3ba901192878f2354b981a15508c8373c|" + "c5eb0a8fcf7e8255a40f821cb815e1096619efeb"; const std::string network_stats_checksum = - "f89a9533dbb35a4c449b44c3ed120f7f1c7f90b6"; + "3d043e47e5f4bb81d37e7bce8c44bf802965c853"; DecodeAndCompare(input_rtp_file, output_checksum, network_stats_checksum, absl::GetFlag(FLAGS_gen_ref)); diff --git a/modules/audio_coding/neteq/packet_buffer_unittest.cc b/modules/audio_coding/neteq/packet_buffer_unittest.cc index 4286006b6e..1a054daca3 100644 --- a/modules/audio_coding/neteq/packet_buffer_unittest.cc +++ b/modules/audio_coding/neteq/packet_buffer_unittest.cc @@ -316,7 +316,7 @@ TEST(PacketBuffer, InsertPacketList) { MockDecoderDatabase decoder_database; auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(0)) .WillRepeatedly(Return(&info)); @@ -366,11 +366,11 @@ TEST(PacketBuffer, InsertPacketListChangePayloadType) { MockDecoderDatabase decoder_database; auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info0(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(0)) .WillRepeatedly(Return(&info0)); const DecoderDatabase::DecoderInfo info1(SdpAudioFormat("pcma", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(1)) .WillRepeatedly(Return(&info1)); @@ -562,7 +562,7 @@ TEST(PacketBuffer, Reordering) { MockDecoderDatabase decoder_database; auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(0)) .WillRepeatedly(Return(&info)); absl::optional current_pt; @@ -609,11 +609,11 @@ TEST(PacketBuffer, CngFirstThenSpeechWithNewSampleRate) { MockDecoderDatabase decoder_database; auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info_cng(SdpAudioFormat("cn", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(kCngPt)) .WillRepeatedly(Return(&info_cng)); const DecoderDatabase::DecoderInfo info_speech( - SdpAudioFormat("l16", 16000, 1), absl::nullopt, factory); + SdpAudioFormat("l16", 16000, 1), absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(kSpeechPt)) .WillRepeatedly(Return(&info_speech)); @@ -736,7 +736,7 @@ TEST(PacketBuffer, Failures) { list.push_back(gen.NextPacket(payload_len, nullptr)); // Valid packet. auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); EXPECT_CALL(decoder_database, GetDecoderInfo(0)) .WillRepeatedly(Return(&info)); absl::optional current_pt; diff --git a/modules/audio_coding/neteq/statistics_calculator.cc b/modules/audio_coding/neteq/statistics_calculator.cc index 8e281302b7..a0739a9345 100644 --- a/modules/audio_coding/neteq/statistics_calculator.cc +++ b/modules/audio_coding/neteq/statistics_calculator.cc @@ -230,6 +230,10 @@ void StatisticsCalculator::AcceleratedSamples(size_t num_samples) { lifetime_stats_.removed_samples_for_acceleration += num_samples; } +void StatisticsCalculator::GeneratedNoiseSamples(size_t num_samples) { + lifetime_stats_.generated_noise_samples += num_samples; +} + void StatisticsCalculator::PacketsDiscarded(size_t num_packets) { operations_and_state_.discarded_primary_packets += num_packets; } diff --git a/modules/audio_coding/neteq/statistics_calculator.h b/modules/audio_coding/neteq/statistics_calculator.h index 269e6a09b2..e9b3d0328f 100644 --- a/modules/audio_coding/neteq/statistics_calculator.h +++ b/modules/audio_coding/neteq/statistics_calculator.h @@ -64,6 +64,9 @@ class StatisticsCalculator { // Reports that `num_samples` samples were removed through accelerate. void AcceleratedSamples(size_t num_samples); + // Reports that `num_samples` comfort noise samples were generated. + void GeneratedNoiseSamples(size_t num_samples); + // Reports that `num_packets` packets were discarded. virtual void PacketsDiscarded(size_t num_packets); diff --git a/modules/audio_coding/neteq/test/neteq_decoding_test.cc b/modules/audio_coding/neteq/test/neteq_decoding_test.cc index 6f27cdad4f..fc49819cf9 100644 --- a/modules/audio_coding/neteq/test/neteq_decoding_test.cc +++ b/modules/audio_coding/neteq/test/neteq_decoding_test.cc @@ -225,7 +225,6 @@ void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, // Insert speech for 2 seconds. const int kSpeechDurationMs = 2000; - int packets_inserted = 0; uint16_t last_seq_no; uint32_t last_timestamp; bool timestamp_wrapped = false; @@ -240,7 +239,6 @@ void NetEqDecodingTest::WrapTest(uint16_t start_seq_no, if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) { // This sequence number was not in the set to drop. Insert it. ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload)); - ++packets_inserted; } NetEqNetworkStatistics network_stats; ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats)); diff --git a/modules/audio_coding/neteq/test/result_sink.cc b/modules/audio_coding/neteq/test/result_sink.cc index b70016180e..0822e00ebe 100644 --- a/modules/audio_coding/neteq/test/result_sink.cc +++ b/modules/audio_coding/neteq/test/result_sink.cc @@ -10,8 +10,9 @@ #include "modules/audio_coding/neteq/test/result_sink.h" -#include +#include +#include "absl/strings/string_view.h" #include "rtc_base/ignore_wundef.h" #include "rtc_base/message_digest.h" #include "rtc_base/string_encode.h" @@ -91,10 +92,10 @@ void ResultSink::AddResult(const NetEqNetworkStatistics& stats_raw) { } void ResultSink::VerifyChecksum(const std::string& checksum) { - std::vector buffer; + std::string buffer; buffer.resize(digest_->Size()); - digest_->Finish(&buffer[0], buffer.size()); - const std::string result = rtc::hex_encode(&buffer[0], digest_->Size()); + digest_->Finish(buffer.data(), buffer.size()); + const std::string result = rtc::hex_encode(buffer); if (checksum.size() == result.size()) { EXPECT_EQ(checksum, result); } else { diff --git a/modules/audio_coding/neteq/timestamp_scaler_unittest.cc b/modules/audio_coding/neteq/timestamp_scaler_unittest.cc index 26dc06db5e..c2bb4dd95f 100644 --- a/modules/audio_coding/neteq/timestamp_scaler_unittest.cc +++ b/modules/audio_coding/neteq/timestamp_scaler_unittest.cc @@ -27,7 +27,7 @@ TEST(TimestampScaler, TestNoScaling) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use PCMu, because it doesn't use scaled timestamps. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 0; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -49,7 +49,7 @@ TEST(TimestampScaler, TestNoScalingLargeStep) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use PCMu, because it doesn't use scaled timestamps. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("pcmu", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 0; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -76,7 +76,7 @@ TEST(TimestampScaler, TestG722) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -102,7 +102,7 @@ TEST(TimestampScaler, TestG722LargeStep) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -132,9 +132,9 @@ TEST(TimestampScaler, TestG722WithCng) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info_g722(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); const DecoderDatabase::DecoderInfo info_cng(SdpAudioFormat("cn", 16000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadTypeG722 = 17; static const uint8_t kRtpPayloadTypeCng = 13; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadTypeG722)) @@ -176,7 +176,7 @@ TEST(TimestampScaler, TestG722Packet) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -206,7 +206,7 @@ TEST(TimestampScaler, TestG722PacketList) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -240,7 +240,7 @@ TEST(TimestampScaler, TestG722Reset) { auto factory = CreateBuiltinAudioDecoderFactory(); // Use G722, which has a factor 2 scaling. const DecoderDatabase::DecoderInfo info(SdpAudioFormat("g722", 8000, 1), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); @@ -281,7 +281,7 @@ TEST(TimestampScaler, TestOpusLargeStep) { MockDecoderDatabase db; auto factory = CreateBuiltinAudioDecoderFactory(); const DecoderDatabase::DecoderInfo info(SdpAudioFormat("opus", 48000, 2), - absl::nullopt, factory); + absl::nullopt, factory.get()); static const uint8_t kRtpPayloadType = 17; EXPECT_CALL(db, GetDecoderInfo(kRtpPayloadType)) .WillRepeatedly(Return(&info)); diff --git a/modules/audio_coding/neteq/tools/audio_checksum.h b/modules/audio_coding/neteq/tools/audio_checksum.h index 9d6f3432c0..42e3a3a3a0 100644 --- a/modules/audio_coding/neteq/tools/audio_checksum.h +++ b/modules/audio_coding/neteq/tools/audio_checksum.h @@ -50,8 +50,7 @@ class AudioChecksum : public AudioSink { finished_ = true; checksum_->Finish(checksum_result_.data(), checksum_result_.size()); } - return rtc::hex_encode(checksum_result_.data(), - checksum_result_.size()); + return rtc::hex_encode(checksum_result_); } private: diff --git a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc index 3f06b1cfc4..db901c6a49 100644 --- a/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc +++ b/modules/audio_coding/neteq/tools/neteq_stats_plotter.cc @@ -94,6 +94,12 @@ void NetEqStatsPlotter::SimulationEnded(int64_t simulation_time_ms) { printf(" interruption_ratio: %f\n", static_cast(lifetime_stats.total_interruption_duration_ms) / simulation_time_ms); + printf(" removed_samples_for_acceleration: %" PRIu64 "\n", + lifetime_stats.removed_samples_for_acceleration); + printf(" inserted_samples_for_deceleration: %" PRIu64 "\n", + lifetime_stats.inserted_samples_for_deceleration); + printf(" generated_noise_samples: %" PRIu64 "\n", + lifetime_stats.generated_noise_samples); } } diff --git a/modules/audio_coding/test/Channel.cc b/modules/audio_coding/test/Channel.cc index ec0eccb5d9..b85f9f416a 100644 --- a/modules/audio_coding/test/Channel.cc +++ b/modules/audio_coding/test/Channel.cc @@ -10,10 +10,8 @@ #include "modules/audio_coding/test/Channel.h" - #include -#include "rtc_base/format_macros.h" #include "rtc_base/time_utils.h" namespace webrtc { diff --git a/modules/audio_coding/test/TestRedFec.cc b/modules/audio_coding/test/TestRedFec.cc index d2c8d8a132..892fbc83d6 100644 --- a/modules/audio_coding/test/TestRedFec.cc +++ b/modules/audio_coding/test/TestRedFec.cc @@ -190,7 +190,8 @@ void TestRedFec::RegisterSendCodec( AudioEncoderCopyRed::Config config; config.payload_type = red_payload_type; config.speech_encoder = std::move(encoder); - encoder = std::make_unique(std::move(config)); + encoder = std::make_unique(std::move(config), + field_trials_); receive_codecs.emplace( std::make_pair(red_payload_type, SdpAudioFormat("red", codec_format.clockrate_hz, 1))); diff --git a/modules/audio_coding/test/TestRedFec.h b/modules/audio_coding/test/TestRedFec.h index 0e92d27330..dbadd88487 100644 --- a/modules/audio_coding/test/TestRedFec.h +++ b/modules/audio_coding/test/TestRedFec.h @@ -19,6 +19,7 @@ #include "common_audio/vad/include/vad.h" #include "modules/audio_coding/test/Channel.h" #include "modules/audio_coding/test/PCMFile.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -37,6 +38,7 @@ class TestRedFec { void Run(); void OpenOutFile(int16_t testNumber); + test::ScopedKeyValueConfig field_trials_; const rtc::scoped_refptr encoder_factory_; const rtc::scoped_refptr decoder_factory_; std::unique_ptr _acmA; diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn index d441479b33..61b2dfd767 100644 --- a/modules/audio_device/BUILD.gn +++ b/modules/audio_device/BUILD.gn @@ -51,7 +51,7 @@ rtc_source_set("audio_device_api") { "../../api:scoped_refptr", "../../api/task_queue", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:refcount", "../../rtc_base:stringutils", ] } @@ -70,9 +70,14 @@ rtc_library("audio_device_buffer") { "../../api:sequence_checker", "../../api/task_queue", "../../common_audio:common_audio_c", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:rtc_task_queue", + "../../rtc_base:safe_conversions", + "../../rtc_base:timestamp_aligner", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../../system_wrappers:metrics", @@ -87,7 +92,7 @@ rtc_library("audio_device_generic") { deps = [ ":audio_device_api", ":audio_device_buffer", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", ] } @@ -110,7 +115,11 @@ rtc_source_set("windows_core_audio_utility") { ":audio_device_name", "../../api/units:time_delta", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread_types", + "../../rtc_base:stringutils", + "../../rtc_base/win:windows_version", ] libs = [ "oleaut32.lib" ] @@ -144,9 +153,18 @@ rtc_source_set("audio_device_module_from_input_and_output") { ":audio_device_buffer", ":windows_core_audio_utility", "../../api:scoped_refptr", + "../../api:sequence_checker", "../../api/task_queue", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:refcount", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", + "../../rtc_base/win:scoped_com_initializer", + "../../rtc_base/win:windows_version", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -169,9 +187,18 @@ rtc_library("audio_device_impl") { "../../common_audio", "../../common_audio:common_audio_c", "../../rtc_base", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:random", + "../../rtc_base:refcount", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_task_queue", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:arch", "../../rtc_base/system:file_wrapper", @@ -318,7 +345,10 @@ rtc_library("audio_device_impl") { "msdmo.lib", "oleaut32.lib", ] - deps += [ "../../rtc_base:win32" ] + deps += [ + "../../rtc_base:win32", + "../../rtc_base/win:scoped_com_initializer", + ] } configs += [ ":audio_device_warnings_config" ] } @@ -387,9 +417,15 @@ if (rtc_include_tests && !build_with_chromium) { "../../api/task_queue", "../../api/task_queue:default_task_queue_factory", "../../common_audio", + "../../rtc_base:buffer", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:race_checker", + "../../rtc_base:rtc_event", + "../../rtc_base:safe_conversions", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../../test:fileutils", @@ -405,14 +441,11 @@ if (rtc_include_tests && !build_with_chromium) { deps += [ ":audio_device_module_from_input_and_output", ":windows_core_audio_utility", + "../../rtc_base/win:scoped_com_initializer", + "../../rtc_base/win:windows_version", ] } if (is_android) { - # Need to disable error due to the line in - # base/android/jni_android.h triggering it: - # const BASE_EXPORT jobject GetApplicationContext() - # error: type qualifiers ignored on function return type - cflags = [ "-Wno-ignored-qualifiers" ] sources += [ "android/audio_device_unittest.cc", "android/audio_manager_unittest.cc", @@ -420,8 +453,10 @@ if (rtc_include_tests && !build_with_chromium) { "android/ensure_initialized.h", ] deps += [ - "../../base", + "../../sdk/android:internal_jni", "../../sdk/android:libjingle_peerconnection_java", + "../../sdk/android:native_api_jni", + "../../sdk/android:native_test_jni_onload", ] } if (!rtc_include_internal_audio_device) { diff --git a/modules/audio_device/DEPS b/modules/audio_device/DEPS index fc5eed7833..9cc627d330 100644 --- a/modules/audio_device/DEPS +++ b/modules/audio_device/DEPS @@ -5,7 +5,7 @@ include_rules = [ specific_include_rules = { "ensure_initialized\.cc": [ - "+base/android", + "+sdk/android", ], "audio_device_impl\.cc": [ "+sdk/objc", diff --git a/modules/audio_device/android/audio_device_unittest.cc b/modules/audio_device/android/audio_device_unittest.cc index 79cd69f2f1..4e607bc446 100644 --- a/modules/audio_device/android/audio_device_unittest.cc +++ b/modules/audio_device/android/audio_device_unittest.cc @@ -29,7 +29,6 @@ #include "modules/audio_device/include/mock_audio_transport.h" #include "rtc_base/arraysize.h" #include "rtc_base/event.h" -#include "rtc_base/format_macros.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/time_utils.h" #include "test/gmock.h" @@ -187,7 +186,7 @@ class FifoAudioStream : public AudioStreamInterface { const size_t size = fifo_->size(); if (size > largest_size_) { largest_size_ = size; - PRINTD("(%" RTC_PRIuS ")", largest_size_); + PRINTD("(%zu)", largest_size_); } total_written_elements_ += size; } @@ -532,13 +531,12 @@ class AudioDeviceTest : public ::testing::Test { #ifdef ENABLE_PRINTF PRINT("file name: %s\n", file_name.c_str()); const size_t bytes = test::GetFileSize(file_name); - PRINT("file size: %" RTC_PRIuS " [bytes]\n", bytes); - PRINT("file size: %" RTC_PRIuS " [samples]\n", bytes / kBytesPerSample); + PRINT("file size: %zu [bytes]\n", bytes); + PRINT("file size: %zu [samples]\n", bytes / kBytesPerSample); const int seconds = static_cast(bytes / (sample_rate * kBytesPerSample)); PRINT("file size: %d [secs]\n", seconds); - PRINT("file size: %" RTC_PRIuS " [callbacks]\n", - seconds * kNumCallbacksPerSecond); + PRINT("file size: %zu [callbacks]\n", seconds * kNumCallbacksPerSecond); #endif return file_name; } diff --git a/modules/audio_device/android/audio_manager_unittest.cc b/modules/audio_device/android/audio_manager_unittest.cc index 1b81904c34..093eddd2e8 100644 --- a/modules/audio_device/android/audio_manager_unittest.cc +++ b/modules/audio_device/android/audio_manager_unittest.cc @@ -15,7 +15,6 @@ #include "modules/audio_device/android/build_info.h" #include "modules/audio_device/android/ensure_initialized.h" #include "rtc_base/arraysize.h" -#include "rtc_base/format_macros.h" #include "test/gtest.h" #define PRINT(...) fprintf(stderr, __VA_ARGS__); @@ -153,16 +152,16 @@ TEST_F(AudioManagerTest, ShowAudioParameterInfo) { PRINT("%saudio layer: %s\n", kTag, low_latency_out ? "Low latency OpenSL" : "Java/JNI based AudioTrack"); PRINT("%ssample rate: %d Hz\n", kTag, playout_parameters_.sample_rate()); - PRINT("%schannels: %" RTC_PRIuS "\n", kTag, playout_parameters_.channels()); - PRINT("%sframes per buffer: %" RTC_PRIuS " <=> %.2f ms\n", kTag, + PRINT("%schannels: %zu\n", kTag, playout_parameters_.channels()); + PRINT("%sframes per buffer: %zu <=> %.2f ms\n", kTag, playout_parameters_.frames_per_buffer(), playout_parameters_.GetBufferSizeInMilliseconds()); PRINT("RECORD: \n"); PRINT("%saudio layer: %s\n", kTag, low_latency_in ? "Low latency OpenSL" : "Java/JNI based AudioRecord"); PRINT("%ssample rate: %d Hz\n", kTag, record_parameters_.sample_rate()); - PRINT("%schannels: %" RTC_PRIuS "\n", kTag, record_parameters_.channels()); - PRINT("%sframes per buffer: %" RTC_PRIuS " <=> %.2f ms\n", kTag, + PRINT("%schannels: %zu\n", kTag, record_parameters_.channels()); + PRINT("%sframes per buffer: %zu <=> %.2f ms\n", kTag, record_parameters_.frames_per_buffer(), record_parameters_.GetBufferSizeInMilliseconds()); } diff --git a/modules/audio_device/android/audio_record_jni.cc b/modules/audio_device/android/audio_record_jni.cc index 9d7bf73097..919eabb983 100644 --- a/modules/audio_device/android/audio_record_jni.cc +++ b/modules/audio_device/android/audio_record_jni.cc @@ -16,7 +16,6 @@ #include "modules/audio_device/android/audio_common.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/logging.h" #include "rtc_base/platform_thread.h" #include "rtc_base/time_utils.h" diff --git a/modules/audio_device/android/audio_track_jni.cc b/modules/audio_device/android/audio_track_jni.cc index 178ccadfdb..5afa1ec252 100644 --- a/modules/audio_device/android/audio_track_jni.cc +++ b/modules/audio_device/android/audio_track_jni.cc @@ -15,7 +15,6 @@ #include "modules/audio_device/android/audio_manager.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/logging.h" #include "rtc_base/platform_thread.h" #include "system_wrappers/include/field_trial.h" diff --git a/modules/audio_device/android/ensure_initialized.cc b/modules/audio_device/android/ensure_initialized.cc index 37086cc9eb..59e9c8f7a6 100644 --- a/modules/audio_device/android/ensure_initialized.cc +++ b/modules/audio_device/android/ensure_initialized.cc @@ -10,19 +10,13 @@ #include "modules/audio_device/android/ensure_initialized.h" +#include #include +#include -#include "rtc_base/ignore_wundef.h" - -// Note: this dependency is dangerous since it reaches into Chromium's base. -// There's a risk of e.g. macro clashes. This file may only be used in tests. -RTC_PUSH_IGNORING_WUNDEF() -#include "base/android/jni_android.h" -RTC_POP_IGNORING_WUNDEF() -#include "modules/audio_device/android/audio_record_jni.h" -#include "modules/audio_device/android/audio_track_jni.h" #include "modules/utility/include/jvm_android.h" #include "rtc_base/checks.h" +#include "sdk/android/src/jni/jvm.h" namespace webrtc { namespace audiodevicemodule { @@ -30,8 +24,9 @@ namespace audiodevicemodule { static pthread_once_t g_initialize_once = PTHREAD_ONCE_INIT; void EnsureInitializedOnce() { - RTC_CHECK(::base::android::IsVMInitialized()); - JNIEnv* jni = ::base::android::AttachCurrentThread(); + RTC_CHECK(::webrtc::jni::GetJVM() != nullptr); + + JNIEnv* jni = ::webrtc::jni::AttachCurrentThreadIfNeeded(); JavaVM* jvm = NULL; RTC_CHECK_EQ(0, jni->GetJavaVM(&jvm)); diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java index 11ed669c3e..92f1c93524 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java @@ -95,8 +95,6 @@ public static boolean isNoiseSuppressorBlacklisted() { // Returns true if the platform AEC should be excluded based on its UUID. // AudioEffect.queryEffects() can throw IllegalStateException. private static boolean isAcousticEchoCancelerExcludedByUUID() { - if (Build.VERSION.SDK_INT < 18) - return false; for (Descriptor d : getAvailableEffects()) { if (d.type.equals(AudioEffect.EFFECT_TYPE_AEC) && d.uuid.equals(AOSP_ACOUSTIC_ECHO_CANCELER)) { @@ -109,8 +107,6 @@ private static boolean isAcousticEchoCancelerExcludedByUUID() { // Returns true if the platform NS should be excluded based on its UUID. // AudioEffect.queryEffects() can throw IllegalStateException. private static boolean isNoiseSuppressorExcludedByUUID() { - if (Build.VERSION.SDK_INT < 18) - return false; for (Descriptor d : getAvailableEffects()) { if (d.type.equals(AudioEffect.EFFECT_TYPE_NS) && d.uuid.equals(AOSP_NOISE_SUPPRESSOR)) { return true; @@ -121,15 +117,11 @@ private static boolean isNoiseSuppressorExcludedByUUID() { // Returns true if the device supports Acoustic Echo Cancellation (AEC). private static boolean isAcousticEchoCancelerEffectAvailable() { - if (Build.VERSION.SDK_INT < 18) - return false; return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_AEC); } // Returns true if the device supports Noise Suppression (NS). private static boolean isNoiseSuppressorEffectAvailable() { - if (Build.VERSION.SDK_INT < 18) - return false; return isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_NS); } @@ -277,9 +269,6 @@ public void release() { // As an example: Samsung Galaxy S6 includes an AGC in the descriptor but // AutomaticGainControl.isAvailable() returns false. private boolean effectTypeIsVoIP(UUID type) { - if (Build.VERSION.SDK_INT < 18) - return false; - return (AudioEffect.EFFECT_TYPE_AEC.equals(type) && isAcousticEchoCancelerSupported()) || (AudioEffect.EFFECT_TYPE_NS.equals(type) && isNoiseSuppressorSupported()); } diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java index b057c3a454..43c416f5b1 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioManager.java @@ -258,7 +258,7 @@ public boolean isLowLatencyInputSupported() { // as well. The NDK doc states that: "As of API level 21, lower latency // audio input is supported on select devices. To take advantage of this // feature, first confirm that lower latency output is available". - return Build.VERSION.SDK_INT >= 21 && isLowLatencyOutputSupported(); + return isLowLatencyOutputSupported(); } // Returns true if the device has professional audio level of functionality @@ -301,9 +301,6 @@ private int getNativeOutputSampleRate() { } private int getSampleRateForApiLevel() { - if (Build.VERSION.SDK_INT < 17) { - return WebRtcAudioUtils.getDefaultSampleRateHz(); - } String sampleRateString = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); return (sampleRateString == null) ? WebRtcAudioUtils.getDefaultSampleRateHz() : Integer.parseInt(sampleRateString); @@ -312,9 +309,6 @@ private int getSampleRateForApiLevel() { // Returns the native output buffer size for low-latency output streams. private int getLowLatencyOutputFramesPerBuffer() { assertTrue(isLowLatencyOutputSupported()); - if (Build.VERSION.SDK_INT < 17) { - return DEFAULT_FRAME_PER_BUFFER; - } String framesPerBuffer = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); return framesPerBuffer == null ? DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer); diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java index e3988e1a36..3e1875c3d6 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioTrack.java @@ -10,7 +10,6 @@ package org.webrtc.voiceengine; -import android.annotation.TargetApi; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFormat; @@ -46,7 +45,7 @@ public class WebRtcAudioTrack { // By default, WebRTC creates audio tracks with a usage attribute // corresponding to voice communications, such as telephony or VoIP. - private static final int DEFAULT_USAGE = getDefaultUsageAttribute(); + private static final int DEFAULT_USAGE = AudioAttributes.USAGE_VOICE_COMMUNICATION; private static int usageAttribute = DEFAULT_USAGE; // This method overrides the default usage attribute and allows the user @@ -60,15 +59,6 @@ public static synchronized void setAudioTrackUsageAttribute(int usage) { usageAttribute = usage; } - private static int getDefaultUsageAttribute() { - if (Build.VERSION.SDK_INT >= 21) { - return AudioAttributes.USAGE_VOICE_COMMUNICATION; - } else { - // Not used on SDKs lower than 21. - return 0; - } - } - private final long nativeAudioTrack; private final AudioManager audioManager; private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker(); @@ -154,7 +144,7 @@ public void run() { byteBuffer.put(emptyBytes); byteBuffer.position(0); } - int bytesWritten = writeBytes(audioTrack, byteBuffer, sizeInBytes); + int bytesWritten = audioTrack.write(byteBuffer, sizeInBytes, AudioTrack.WRITE_BLOCKING); if (bytesWritten != sizeInBytes) { Logging.e(TAG, "AudioTrack.write played invalid number of bytes: " + bytesWritten); // If a write() returns a negative value, an error has occurred. @@ -188,14 +178,6 @@ public void run() { } } - private int writeBytes(AudioTrack audioTrack, ByteBuffer byteBuffer, int sizeInBytes) { - if (Build.VERSION.SDK_INT >= 21) { - return audioTrack.write(byteBuffer, sizeInBytes, AudioTrack.WRITE_BLOCKING); - } else { - return audioTrack.write(byteBuffer.array(), byteBuffer.arrayOffset(), sizeInBytes); - } - } - // Stops the inner thread loop which results in calling AudioTrack.stop(). // Does not block the calling thread. public void stopThread() { @@ -257,19 +239,12 @@ private int initPlayout(int sampleRate, int channels, double bufferSizeFactor) { // Create an AudioTrack object and initialize its associated audio buffer. // The size of this buffer determines how long an AudioTrack can play // before running out of data. - if (Build.VERSION.SDK_INT >= 21) { - // If we are on API level 21 or higher, it is possible to use a special AudioTrack - // constructor that uses AudioAttributes and AudioFormat as input. It allows us to - // supersede the notion of stream types for defining the behavior of audio playback, - // and to allow certain platforms or routing policies to use this information for more - // refined volume or routing decisions. - audioTrack = createAudioTrackOnLollipopOrHigher( - sampleRate, channelConfig, minBufferSizeInBytes); - } else { - // Use default constructor for API levels below 21. - audioTrack = - createAudioTrackOnLowerThanLollipop(sampleRate, channelConfig, minBufferSizeInBytes); - } + // As we are on API level 21 or higher, it is possible to use a special AudioTrack + // constructor that uses AudioAttributes and AudioFormat as input. It allows us to + // supersede the notion of stream types for defining the behavior of audio playback, + // and to allow certain platforms or routing policies to use this information for more + // refined volume or routing decisions. + audioTrack = createAudioTrack(sampleRate, channelConfig, minBufferSizeInBytes); } catch (IllegalArgumentException e) { reportWebRtcAudioTrackInitError(e.getMessage()); releaseAudioResources(); @@ -353,7 +328,7 @@ private boolean setStreamVolume(int volume) { threadChecker.checkIsOnValidThread(); Logging.d(TAG, "setStreamVolume(" + volume + ")"); assertTrue(audioManager != null); - if (isVolumeFixed()) { + if (audioManager.isVolumeFixed()) { Logging.e(TAG, "The device implements a fixed volume policy."); return false; } @@ -361,12 +336,6 @@ private boolean setStreamVolume(int volume) { return true; } - private boolean isVolumeFixed() { - if (Build.VERSION.SDK_INT < 21) - return false; - return audioManager.isVolumeFixed(); - } - /** Get current volume level for a phone call audio stream. */ private int getStreamVolume() { threadChecker.checkIsOnValidThread(); @@ -387,10 +356,9 @@ private void logMainParameters() { // Creates and AudioTrack instance using AudioAttributes and AudioFormat as input. // It allows certain platforms or routing policies to use this information for more // refined volume or routing decisions. - @TargetApi(21) - private static AudioTrack createAudioTrackOnLollipopOrHigher( + private static AudioTrack createAudioTrack( int sampleRateInHz, int channelConfig, int bufferSizeInBytes) { - Logging.d(TAG, "createAudioTrackOnLollipopOrHigher"); + Logging.d(TAG, "createAudioTrack"); // TODO(henrika): use setPerformanceMode(int) with PERFORMANCE_MODE_LOW_LATENCY to control // performance when Android O is supported. Add some logging in the mean time. final int nativeOutputSampleRate = @@ -418,13 +386,6 @@ private static AudioTrack createAudioTrackOnLollipopOrHigher( AudioManager.AUDIO_SESSION_ID_GENERATE); } - @SuppressWarnings("deprecation") // Deprecated in API level 25. - private static AudioTrack createAudioTrackOnLowerThanLollipop( - int sampleRateInHz, int channelConfig, int bufferSizeInBytes) { - return new AudioTrack(AudioManager.STREAM_VOICE_CALL, sampleRateInHz, channelConfig, - AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes, AudioTrack.MODE_STREAM); - } - private void logBufferSizeInFrames() { if (Build.VERSION.SDK_INT >= 23) { Logging.d(TAG, "AudioTrack: " diff --git a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java index 61cab58f07..0472114297 100644 --- a/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java +++ b/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioUtils.java @@ -214,13 +214,6 @@ private static void logAudioStateBasic(String tag, AudioManager audioManager) { + "BT SCO: " + audioManager.isBluetoothScoOn()); } - private static boolean isVolumeFixed(AudioManager audioManager) { - if (Build.VERSION.SDK_INT < 21) { - return false; - } - return audioManager.isVolumeFixed(); - } - // Adds volume information for all possible stream types. private static void logAudioStateVolume(String tag, AudioManager audioManager) { final int[] streams = { @@ -233,7 +226,7 @@ private static void logAudioStateVolume(String tag, AudioManager audioManager) { }; Logging.d(tag, "Audio State: "); // Some devices may not have volume controls and might use a fixed volume. - boolean fixedVolume = isVolumeFixed(audioManager); + boolean fixedVolume = audioManager.isVolumeFixed(); Logging.d(tag, " fixed volume=" + fixedVolume); if (!fixedVolume) { for (int stream : streams) { diff --git a/modules/audio_device/android/opensles_player.cc b/modules/audio_device/android/opensles_player.cc index b5851f7582..f2b3a37194 100644 --- a/modules/audio_device/android/opensles_player.cc +++ b/modules/audio_device/android/opensles_player.cc @@ -20,7 +20,6 @@ #include "modules/audio_device/fine_audio_buffer.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/platform_thread.h" #include "rtc_base/time_utils.h" @@ -193,7 +192,7 @@ void OpenSLESPlayer::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { ALOGD("SetPlayoutSampleRate(%d)", sample_rate_hz); audio_device_buffer_->SetPlayoutSampleRate(sample_rate_hz); const size_t channels = audio_parameters_.channels(); - ALOGD("SetPlayoutChannels(%" RTC_PRIuS ")", channels); + ALOGD("SetPlayoutChannels(%zu)", channels); audio_device_buffer_->SetPlayoutChannels(channels); RTC_CHECK(audio_device_buffer_); AllocateDataBuffers(); @@ -214,7 +213,7 @@ void OpenSLESPlayer::AllocateDataBuffers() { // which reduces jitter. const size_t buffer_size_in_samples = audio_parameters_.frames_per_buffer() * audio_parameters_.channels(); - ALOGD("native buffer size: %" RTC_PRIuS, buffer_size_in_samples); + ALOGD("native buffer size: %zu", buffer_size_in_samples); ALOGD("native buffer size in ms: %.2f", audio_parameters_.GetBufferSizeInMilliseconds()); fine_audio_buffer_ = std::make_unique(audio_device_buffer_); diff --git a/modules/audio_device/android/opensles_recorder.cc b/modules/audio_device/android/opensles_recorder.cc index 8becd202cc..4e0c26dbf0 100644 --- a/modules/audio_device/android/opensles_recorder.cc +++ b/modules/audio_device/android/opensles_recorder.cc @@ -20,7 +20,6 @@ #include "modules/audio_device/fine_audio_buffer.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/platform_thread.h" #include "rtc_base/time_utils.h" @@ -178,7 +177,7 @@ void OpenSLESRecorder::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) { // Ensure that the audio device buffer is informed about the number of // channels preferred by the OS on the recording side. const size_t channels = audio_parameters_.channels(); - ALOGD("SetRecordingChannels(%" RTC_PRIuS ")", channels); + ALOGD("SetRecordingChannels(%zu)", channels); audio_device_buffer_->SetRecordingChannels(channels); // Allocated memory for internal data buffers given existing audio parameters. AllocateDataBuffers(); @@ -334,12 +333,10 @@ void OpenSLESRecorder::AllocateDataBuffers() { // Create a modified audio buffer class which allows us to deliver any number // of samples (and not only multiple of 10ms) to match the native audio unit // buffer size. - ALOGD("frames per native buffer: %" RTC_PRIuS, - audio_parameters_.frames_per_buffer()); - ALOGD("frames per 10ms buffer: %" RTC_PRIuS, + ALOGD("frames per native buffer: %zu", audio_parameters_.frames_per_buffer()); + ALOGD("frames per 10ms buffer: %zu", audio_parameters_.frames_per_10ms_buffer()); - ALOGD("bytes per native buffer: %" RTC_PRIuS, - audio_parameters_.GetBytesPerBuffer()); + ALOGD("bytes per native buffer: %zu", audio_parameters_.GetBytesPerBuffer()); ALOGD("native sample rate: %d", audio_parameters_.sample_rate()); RTC_DCHECK(audio_device_buffer_); fine_audio_buffer_ = std::make_unique(audio_device_buffer_); diff --git a/modules/audio_device/audio_device_buffer.h b/modules/audio_device/audio_device_buffer.h index ea6ab9a93e..9a6a88a1be 100644 --- a/modules/audio_device/audio_device_buffer.h +++ b/modules/audio_device/audio_device_buffer.h @@ -228,7 +228,7 @@ class AudioDeviceBuffer { // being printed in the LogStats() task. bool log_stats_ RTC_GUARDED_BY(task_queue_); - // Used for converting capture timestaps (recieved from AudioRecordThread + // Used for converting capture timestaps (received from AudioRecordThread // via AudioRecordJni::DataIsRecorded) to RTC clock. rtc::TimestampAligner timestamp_aligner_; diff --git a/modules/audio_device/dummy/file_audio_device.cc b/modules/audio_device/dummy/file_audio_device.cc index e345a16c44..0e3caa9d4f 100644 --- a/modules/audio_device/dummy/file_audio_device.cc +++ b/modules/audio_device/dummy/file_audio_device.cc @@ -206,7 +206,7 @@ int32_t FileAudioDevice::StartPlayout() { // PLAYOUT if (!_outputFilename.empty()) { - _outputFile = FileWrapper::OpenWriteOnly(_outputFilename.c_str()); + _outputFile = FileWrapper::OpenWriteOnly(_outputFilename); if (!_outputFile.is_open()) { RTC_LOG(LS_ERROR) << "Failed to open playout file: " << _outputFilename; _playing = false; @@ -266,7 +266,7 @@ int32_t FileAudioDevice::StartRecording() { } if (!_inputFilename.empty()) { - _inputFile = FileWrapper::OpenReadOnly(_inputFilename.c_str()); + _inputFile = FileWrapper::OpenReadOnly(_inputFilename); if (!_inputFile.is_open()) { RTC_LOG(LS_ERROR) << "Failed to open audio input file: " << _inputFilename; diff --git a/modules/audio_device/win/audio_device_core_win.cc b/modules/audio_device/win/audio_device_core_win.cc index 096e49cc12..a5f1f1438d 100644 --- a/modules/audio_device/win/audio_device_core_win.cc +++ b/modules/audio_device/win/audio_device_core_win.cc @@ -23,7 +23,11 @@ #ifdef WEBRTC_WINDOWS_CORE_AUDIO_BUILD +// clang-format off +// To get Windows includes in the right order, this must come before the Windows +// includes below. #include "modules/audio_device/win/audio_device_core_win.h" +// clang-format on #include @@ -40,6 +44,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/platform_thread.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/string_utils.h" #include "rtc_base/thread_annotations.h" #include "system_wrappers/include/sleep.h" @@ -2090,7 +2095,8 @@ int32_t AudioDeviceWindowsCore::InitRecordingDMO() { << "AudioDeviceBuffer must be attached before streaming can start"; } - _mediaBuffer = new MediaBufferImpl(_recBlockSize * _recAudioFrameSize); + _mediaBuffer = rtc::make_ref_counted(_recBlockSize * + _recAudioFrameSize); // Optional, but if called, must be after media types are set. hr = _dmo->AllocateStreamingResources(); @@ -2996,7 +3002,7 @@ DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() { DWORD dwStatus = 0; { DMO_OUTPUT_DATA_BUFFER dmoBuffer = {0}; - dmoBuffer.pBuffer = _mediaBuffer; + dmoBuffer.pBuffer = _mediaBuffer.get(); dmoBuffer.pBuffer->AddRef(); // Poll the DMO for AEC processed capture data. The DMO will @@ -3393,32 +3399,34 @@ int AudioDeviceWindowsCore::SetDMOProperties() { // Set the AEC system mode. // SINGLE_CHANNEL_AEC - AEC processing only. - if (SetVtI4Property(ps, MFPKEY_WMAAECMA_SYSTEM_MODE, SINGLE_CHANNEL_AEC)) { + if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_SYSTEM_MODE, + SINGLE_CHANNEL_AEC)) { return -1; } // Set the AEC source mode. // VARIANT_TRUE - Source mode (we poll the AEC for captured data). - if (SetBoolProperty(ps, MFPKEY_WMAAECMA_DMO_SOURCE_MODE, VARIANT_TRUE) == - -1) { + if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_DMO_SOURCE_MODE, + VARIANT_TRUE) == -1) { return -1; } // Enable the feature mode. // This lets us override all the default processing settings below. - if (SetBoolProperty(ps, MFPKEY_WMAAECMA_FEATURE_MODE, VARIANT_TRUE) == -1) { + if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_FEATURE_MODE, VARIANT_TRUE) == + -1) { return -1; } // Disable analog AGC (default enabled). - if (SetBoolProperty(ps, MFPKEY_WMAAECMA_MIC_GAIN_BOUNDER, VARIANT_FALSE) == - -1) { + if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_MIC_GAIN_BOUNDER, + VARIANT_FALSE) == -1) { return -1; } // Disable noise suppression (default enabled). // 0 - Disabled, 1 - Enabled - if (SetVtI4Property(ps, MFPKEY_WMAAECMA_FEATR_NS, 0) == -1) { + if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_FEATR_NS, 0) == -1) { return -1; } @@ -3463,7 +3471,8 @@ int AudioDeviceWindowsCore::SetDMOProperties() { static_cast(0x0000ffff & inDevIndex); RTC_LOG(LS_VERBOSE) << "Capture device index: " << inDevIndex << ", render device index: " << outDevIndex; - if (SetVtI4Property(ps, MFPKEY_WMAAECMA_DEVICE_INDEXES, devIndex) == -1) { + if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_DEVICE_INDEXES, devIndex) == + -1) { return -1; } @@ -3766,7 +3775,7 @@ int32_t AudioDeviceWindowsCore::_GetDefaultDeviceIndex(EDataFlow dir, SAFE_RELEASE(ptrDevice); } - if (_GetDeviceID(device, szDeviceID, kDeviceIDLength) == -1) { + if (_GetDeviceID(device.get(), szDeviceID, kDeviceIDLength) == -1) { return -1; } diff --git a/modules/audio_mixer/BUILD.gn b/modules/audio_mixer/BUILD.gn index d51be4af04..1196835fec 100644 --- a/modules/audio_mixer/BUILD.gn +++ b/modules/audio_mixer/BUILD.gn @@ -46,7 +46,10 @@ rtc_library("audio_mixer_impl") { "../../audio/utility:audio_frame_operations", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:race_checker", + "../../rtc_base:refcount", "../../rtc_base:safe_conversions", "../../rtc_base/synchronization:mutex", "../../system_wrappers", @@ -73,7 +76,6 @@ rtc_library("audio_frame_manipulator") { "../../api/audio:audio_frame_api", "../../audio/utility:audio_frame_operations", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", ] } @@ -94,7 +96,7 @@ if (rtc_include_tests) { "../../api:array_view", "../../api/audio:audio_frame_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", ] } @@ -117,7 +119,7 @@ if (rtc_include_tests) { "../../api/units:timestamp", "../../audio/utility:audio_frame_operations", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", "../../test:test_support", ] diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index ee6b579617..a3ab4ec5c7 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -33,7 +33,9 @@ rtc_library("api") { "../../api/audio:aec3_config", "../../api/audio:audio_frame_api", "../../api/audio:echo_control", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:refcount", + "../../rtc_base:stringutils", "../../rtc_base/system:arch", "../../rtc_base/system:file_wrapper", "../../rtc_base/system:rtc_export", @@ -151,8 +153,6 @@ rtc_library("audio_processing") { "gain_control_impl.cc", "gain_control_impl.h", "render_queue_item_verifier.h", - "typing_detection.cc", - "typing_detection.h", ] defines = [] @@ -176,12 +176,18 @@ rtc_library("audio_processing") { "../../audio/utility:audio_frame_operations", "../../common_audio:common_audio_c", "../../common_audio/third_party/ooura:fft_size_256", + "../../rtc_base:atomicops", "../../rtc_base:checks", + "../../rtc_base:event_tracer", "../../rtc_base:gtest_prod", "../../rtc_base:ignore_wundef", + "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:refcount", "../../rtc_base:safe_minmax", "../../rtc_base:sanitizer", + "../../rtc_base:swap_queue", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:rtc_export", "../../system_wrappers", @@ -200,13 +206,15 @@ rtc_library("audio_processing") { "transient:transient_suppressor_api", "vad", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] deps += [ "../../common_audio", "../../common_audio:fir_filter", "../../common_audio:fir_filter_factory", - "../../rtc_base:rtc_base_approved", "../../system_wrappers", ] @@ -236,9 +244,9 @@ rtc_library("residual_echo_detector") { ":api", ":apm_logging", "../../api:array_view", + "../../rtc_base:atomicops", "../../rtc_base:checks", "../../rtc_base:logging", - "../../rtc_base:rtc_base_approved", "../../system_wrappers:metrics", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -301,7 +309,7 @@ rtc_library("apm_logging") { "../../api:array_view", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] defines = [] @@ -375,10 +383,17 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../rtc_base:gtest_prod", "../../rtc_base:ignore_wundef", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", + "../../rtc_base:refcount", "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:rtc_event", + "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", + "../../rtc_base:stringutils", + "../../rtc_base:swap_queue", "../../rtc_base:task_queue_for_test", "../../rtc_base:threading", "../../rtc_base/synchronization:mutex", @@ -472,8 +487,12 @@ if (rtc_include_tests) { ":audio_processing", ":audioproc_test_utils", "../../api:array_view", + "../../rtc_base:atomicops", + "../../rtc_base:platform_thread", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", + "../../rtc_base:rtc_event", + "../../rtc_base:safe_conversions", "../../system_wrappers", "../../test:perf_test", "../../test:test_support", @@ -490,7 +509,8 @@ if (rtc_include_tests) { "../../api/audio:audio_frame_api", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", "agc:gain_map", ] @@ -529,10 +549,13 @@ if (rtc_include_tests) { "../../common_audio", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", + "../../rtc_base:logging", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_json", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", + "../../rtc_base:timeutils", "../../rtc_base/system:file_wrapper", "../../system_wrappers", "../../system_wrappers:field_trial", @@ -567,7 +590,6 @@ if (rtc_include_tests) { "../../rtc_base:checks", "../../rtc_base:ignore_wundef", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", "../../rtc_base/system:arch", ] } @@ -617,7 +639,8 @@ rtc_library("audioproc_test_utils") { "../../api/audio:audio_frame_api", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", + "../../rtc_base:refcount", "../../rtc_base/system:arch", "../../system_wrappers", "../../test:fileutils", diff --git a/modules/audio_processing/aec3/BUILD.gn b/modules/audio_processing/aec3/BUILD.gn index 3ce494346f..8876002990 100644 --- a/modules/audio_processing/aec3/BUILD.gn +++ b/modules/audio_processing/aec3/BUILD.gn @@ -37,6 +37,8 @@ rtc_library("aec3") { "coarse_filter_update_gain.h", "comfort_noise_generator.cc", "comfort_noise_generator.h", + "config_selector.cc", + "config_selector.h", "decimator.cc", "decimator.h", "delay_estimate.h", @@ -72,6 +74,8 @@ rtc_library("aec3") { "matched_filter_lag_aggregator.h", "moving_average.cc", "moving_average.h", + "multi_channel_content_detector.cc", + "multi_channel_content_detector.h", "nearend_detector.h", "refined_filter_update_gain.cc", "refined_filter_update_gain.h", @@ -139,9 +143,13 @@ rtc_library("aec3") { "../../../api/audio:aec3_config", "../../../api/audio:echo_control", "../../../common_audio:common_audio_c", + "../../../rtc_base:atomicops", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:macromagic", + "../../../rtc_base:race_checker", "../../../rtc_base:safe_minmax", + "../../../rtc_base:swap_queue", "../../../rtc_base/experiments:field_trial_parser", "../../../rtc_base/system:arch", "../../../system_wrappers", @@ -168,7 +176,6 @@ rtc_source_set("aec3_fft") { "../../../api:array_view", "../../../common_audio/third_party/ooura:fft_size_128", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:arch", ] } @@ -185,7 +192,6 @@ rtc_source_set("render_buffer") { ":fft_data", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:arch", ] } @@ -217,7 +223,6 @@ rtc_source_set("matched_filter") { deps = [ ":aec3_common", "../../../api:array_view", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:arch", ] } @@ -306,10 +311,13 @@ if (rtc_include_tests) { "../../../api:array_view", "../../../api/audio:aec3_config", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:macromagic", + "../../../rtc_base:random", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", "../../../rtc_base/system:arch", "../../../system_wrappers", + "../../../system_wrappers:metrics", "../../../test:field_trial", "../../../test:test_support", "../utility:cascaded_biquad_filter", @@ -333,6 +341,7 @@ if (rtc_include_tests) { "clockdrift_detector_unittest.cc", "coarse_filter_update_gain_unittest.cc", "comfort_noise_generator_unittest.cc", + "config_selector_unittest.cc", "decimator_unittest.cc", "echo_canceller3_unittest.cc", "echo_path_delay_estimator_unittest.cc", @@ -347,6 +356,7 @@ if (rtc_include_tests) { "matched_filter_lag_aggregator_unittest.cc", "matched_filter_unittest.cc", "moving_average_unittest.cc", + "multi_channel_content_detector_unittest.cc", "refined_filter_update_gain_unittest.cc", "render_buffer_unittest.cc", "render_delay_buffer_unittest.cc", diff --git a/modules/audio_processing/aec3/config_selector.cc b/modules/audio_processing/aec3/config_selector.cc new file mode 100644 index 0000000000..c55344da79 --- /dev/null +++ b/modules/audio_processing/aec3/config_selector.cc @@ -0,0 +1,71 @@ + +/* + * Copyright (c) 2022 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/aec3/config_selector.h" + +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Validates that the mono and the multichannel configs have compatible fields. +bool CompatibleConfigs(const EchoCanceller3Config& mono_config, + const EchoCanceller3Config& multichannel_config) { + if (mono_config.delay.fixed_capture_delay_samples != + multichannel_config.delay.fixed_capture_delay_samples) { + return false; + } + if (mono_config.filter.export_linear_aec_output != + multichannel_config.filter.export_linear_aec_output) { + return false; + } + if (mono_config.filter.high_pass_filter_echo_reference != + multichannel_config.filter.high_pass_filter_echo_reference) { + return false; + } + if (mono_config.multi_channel.detect_stereo_content != + multichannel_config.multi_channel.detect_stereo_content) { + return false; + } + if (mono_config.multi_channel.stereo_detection_timeout_threshold_seconds != + multichannel_config.multi_channel + .stereo_detection_timeout_threshold_seconds) { + return false; + } + return true; +} + +} // namespace + +ConfigSelector::ConfigSelector( + const EchoCanceller3Config& config, + const absl::optional& multichannel_config, + int num_render_input_channels) + : config_(config), multichannel_config_(multichannel_config) { + if (multichannel_config_.has_value()) { + RTC_DCHECK(CompatibleConfigs(config_, *multichannel_config_)); + } + + Update(!config_.multi_channel.detect_stereo_content && + num_render_input_channels > 1); + + RTC_DCHECK(active_config_); +} + +void ConfigSelector::Update(bool multichannel_content) { + if (multichannel_content && multichannel_config_.has_value()) { + active_config_ = &(*multichannel_config_); + } else { + active_config_ = &config_; + } +} + +} // namespace webrtc diff --git a/modules/audio_processing/aec3/config_selector.h b/modules/audio_processing/aec3/config_selector.h new file mode 100644 index 0000000000..3b3f94e5ac --- /dev/null +++ b/modules/audio_processing/aec3/config_selector.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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_AEC3_CONFIG_SELECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_CONFIG_SELECTOR_H_ + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" + +namespace webrtc { + +// Selects the config to use. +class ConfigSelector { + public: + ConfigSelector( + const EchoCanceller3Config& config, + const absl::optional& multichannel_config, + int num_render_input_channels); + + // Updates the config selection based on the detection of multichannel + // content. + void Update(bool multichannel_content); + + const EchoCanceller3Config& active_config() const { return *active_config_; } + + private: + const EchoCanceller3Config config_; + const absl::optional multichannel_config_; + const EchoCanceller3Config* active_config_ = nullptr; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_CONFIG_SELECTOR_H_ diff --git a/modules/audio_processing/aec3/config_selector_unittest.cc b/modules/audio_processing/aec3/config_selector_unittest.cc new file mode 100644 index 0000000000..1826bfcace --- /dev/null +++ b/modules/audio_processing/aec3/config_selector_unittest.cc @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 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/aec3/config_selector.h" + +#include + +#include "absl/types/optional.h" +#include "api/audio/echo_canceller3_config.h" +#include "test/gtest.h" + +namespace webrtc { + +class ConfigSelectorChannelsAndContentDetection + : public ::testing::Test, + public ::testing::WithParamInterface> {}; + +INSTANTIATE_TEST_SUITE_P(ConfigSelectorMultiParameters, + ConfigSelectorChannelsAndContentDetection, + ::testing::Combine(::testing::Values(1, 2, 8), + ::testing::Values(false, true))); + +class ConfigSelectorChannels : public ::testing::Test, + public ::testing::WithParamInterface {}; + +INSTANTIATE_TEST_SUITE_P(ConfigSelectorMultiParameters, + ConfigSelectorChannels, + ::testing::Values(1, 2, 8)); + +TEST_P(ConfigSelectorChannelsAndContentDetection, + MonoConfigIsSelectedWhenNoMultiChannelConfigPresent) { + const auto [num_channels, detect_stereo_content] = GetParam(); + EchoCanceller3Config config; + config.multi_channel.detect_stereo_content = detect_stereo_content; + absl::optional multichannel_config; + + config.delay.default_delay = config.delay.default_delay + 1; + const size_t custom_delay_value_in_config = config.delay.default_delay; + + ConfigSelector cs(config, multichannel_config, + /*num_render_input_channels=*/num_channels); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); + + cs.Update(/*multichannel_content=*/false); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); + + cs.Update(/*multichannel_content=*/true); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); +} + +TEST_P(ConfigSelectorChannelsAndContentDetection, + CorrectInitialConfigIsSelected) { + const auto [num_channels, detect_stereo_content] = GetParam(); + EchoCanceller3Config config; + config.multi_channel.detect_stereo_content = detect_stereo_content; + absl::optional multichannel_config = config; + + config.delay.default_delay += 1; + const size_t custom_delay_value_in_config = config.delay.default_delay; + multichannel_config->delay.default_delay += 2; + const size_t custom_delay_value_in_multichannel_config = + multichannel_config->delay.default_delay; + + ConfigSelector cs(config, multichannel_config, + /*num_render_input_channels=*/num_channels); + + if (num_channels == 1 || detect_stereo_content) { + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); + } else { + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_multichannel_config); + } +} + +TEST_P(ConfigSelectorChannels, CorrectConfigUpdateBehavior) { + const int num_channels = GetParam(); + EchoCanceller3Config config; + config.multi_channel.detect_stereo_content = true; + absl::optional multichannel_config = config; + + config.delay.default_delay += 1; + const size_t custom_delay_value_in_config = config.delay.default_delay; + multichannel_config->delay.default_delay += 2; + const size_t custom_delay_value_in_multichannel_config = + multichannel_config->delay.default_delay; + + ConfigSelector cs(config, multichannel_config, + /*num_render_input_channels=*/num_channels); + + cs.Update(/*multichannel_content=*/false); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); + + if (num_channels == 1) { + cs.Update(/*multichannel_content=*/false); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_config); + } else { + cs.Update(/*multichannel_content=*/true); + EXPECT_EQ(cs.active_config().delay.default_delay, + custom_delay_value_in_multichannel_config); + } +} + +} // namespace webrtc diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc index 419a056d20..36bf8769f4 100644 --- a/modules/audio_processing/aec3/echo_canceller3.cc +++ b/modules/audio_processing/aec3/echo_canceller3.cc @@ -96,18 +96,50 @@ void FillSubFrameView( } void FillSubFrameView( + bool proper_downmix_needed, std::vector>>* frame, size_t sub_frame_index, std::vector>>* sub_frame_view) { RTC_DCHECK_GE(1, sub_frame_index); RTC_DCHECK_EQ(frame->size(), sub_frame_view->size()); - RTC_DCHECK_EQ((*frame)[0].size(), (*sub_frame_view)[0].size()); - for (size_t band = 0; band < frame->size(); ++band) { - for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) { - (*sub_frame_view)[band][channel] = rtc::ArrayView( - &(*frame)[band][channel][sub_frame_index * kSubFrameLength], + const size_t frame_num_channels = (*frame)[0].size(); + const size_t sub_frame_num_channels = (*sub_frame_view)[0].size(); + if (frame_num_channels > sub_frame_num_channels) { + RTC_DCHECK_EQ(sub_frame_num_channels, 1u); + if (proper_downmix_needed) { + // When a proper downmix is needed (which is the case when proper stereo + // is present in the echo reference signal but the echo canceller does the + // processing in mono) downmix the echo reference by averaging the channel + // content (otherwise downmixing is done by selecting channel 0). + for (size_t band = 0; band < frame->size(); ++band) { + for (size_t ch = 1; ch < frame_num_channels; ++ch) { + for (size_t k = 0; k < kSubFrameLength; ++k) { + (*frame)[band][/*channel=*/0] + [sub_frame_index * kSubFrameLength + k] += + (*frame)[band][ch][sub_frame_index * kSubFrameLength + k]; + } + } + const float one_by_num_channels = 1.0f / frame_num_channels; + for (size_t k = 0; k < kSubFrameLength; ++k) { + (*frame)[band][/*channel=*/0][sub_frame_index * kSubFrameLength + + k] *= one_by_num_channels; + } + } + } + for (size_t band = 0; band < frame->size(); ++band) { + (*sub_frame_view)[band][/*channel=*/0] = rtc::ArrayView( + &(*frame)[band][/*channel=*/0][sub_frame_index * kSubFrameLength], kSubFrameLength); } + } else { + RTC_DCHECK_EQ(frame_num_channels, sub_frame_num_channels); + for (size_t band = 0; band < frame->size(); ++band) { + for (size_t channel = 0; channel < (*frame)[band].size(); ++channel) { + (*sub_frame_view)[band][channel] = rtc::ArrayView( + &(*frame)[band][channel][sub_frame_index * kSubFrameLength], + kSubFrameLength); + } + } } } @@ -115,6 +147,7 @@ void ProcessCaptureFrameContent( AudioBuffer* linear_output, AudioBuffer* capture, bool level_change, + bool aec_reference_is_downmixed_stereo, bool saturated_microphone_signal, size_t sub_frame_index, FrameBlocker* capture_blocker, @@ -138,7 +171,9 @@ void ProcessCaptureFrameContent( capture_blocker->InsertSubFrameAndExtractBlock(*capture_sub_frame_view, capture_block); - block_processor->ProcessCapture(level_change, saturated_microphone_signal, + block_processor->ProcessCapture(/*echo_path_gain_change=*/level_change || + aec_reference_is_downmixed_stereo, + saturated_microphone_signal, linear_output_block, capture_block); output_framer->InsertBlockAndExtractSubFrame(*capture_block, capture_sub_frame_view); @@ -152,6 +187,7 @@ void ProcessCaptureFrameContent( void ProcessRemainingCaptureFrameContent( bool level_change, + bool aec_reference_is_downmixed_stereo, bool saturated_microphone_signal, FrameBlocker* capture_blocker, BlockFramer* linear_output_framer, @@ -164,8 +200,10 @@ void ProcessRemainingCaptureFrameContent( } capture_blocker->ExtractBlock(block); - block_processor->ProcessCapture(level_change, saturated_microphone_signal, - linear_output_block, block); + block_processor->ProcessCapture( + /*echo_path_gain_change=*/level_change || + aec_reference_is_downmixed_stereo, + saturated_microphone_signal, linear_output_block, block); output_framer->InsertBlock(*block); if (linear_output_framer) { @@ -175,13 +213,15 @@ void ProcessRemainingCaptureFrameContent( } void BufferRenderFrameContent( + bool proper_downmix_needed, std::vector>>* render_frame, size_t sub_frame_index, FrameBlocker* render_blocker, BlockProcessor* block_processor, std::vector>>* block, std::vector>>* sub_frame_view) { - FillSubFrameView(render_frame, sub_frame_index, sub_frame_view); + FillSubFrameView(proper_downmix_needed, render_frame, sub_frame_index, + sub_frame_view); render_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block); block_processor->BufferRender(*block); } @@ -221,6 +261,10 @@ void CopyBufferIntoFrame(const AudioBuffer& buffer, EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) { EchoCanceller3Config adjusted_cfg = config; + if (field_trial::IsEnabled("WebRTC-Aec3StereoContentDetectionKillSwitch")) { + adjusted_cfg.multi_channel.detect_stereo_content = false; + } + if (field_trial::IsEnabled("WebRTC-Aec3AntiHowlingMinimizationKillSwitch")) { adjusted_cfg.suppressor.high_bands_suppression .anti_howling_activation_threshold = 25.f; @@ -294,7 +338,6 @@ EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) { adjusted_cfg.ep_strength.use_conservative_tail_frequency_response = false; } - if (field_trial::IsEnabled("WebRTC-Aec3ShortHeadroomKillSwitch")) { // Two blocks headroom. adjusted_cfg.delay.delay_headroom_samples = kBlockSize * 2; @@ -668,80 +711,75 @@ void EchoCanceller3::RenderWriter::Insert(const AudioBuffer& input) { int EchoCanceller3::instance_count_ = 0; -EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, - int sample_rate_hz, - size_t num_render_channels, - size_t num_capture_channels) - : EchoCanceller3(AdjustConfig(config), - sample_rate_hz, - num_render_channels, - num_capture_channels, - std::unique_ptr( - BlockProcessor::Create(AdjustConfig(config), - sample_rate_hz, - num_render_channels, - num_capture_channels))) {} -EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, - int sample_rate_hz, - size_t num_render_channels, - size_t num_capture_channels, - std::unique_ptr block_processor) +EchoCanceller3::EchoCanceller3( + const EchoCanceller3Config& config, + const absl::optional& multichannel_config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels) : data_dumper_( new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))), - config_(config), + config_(AdjustConfig(config)), sample_rate_hz_(sample_rate_hz), num_bands_(NumBandsForRate(sample_rate_hz_)), - num_render_channels_(num_render_channels), + num_render_input_channels_(num_render_channels), num_capture_channels_(num_capture_channels), + config_selector_(AdjustConfig(config), + multichannel_config, + num_render_input_channels_), + multichannel_content_detector_( + config_selector_.active_config().multi_channel.detect_stereo_content, + num_render_input_channels_, + config_selector_.active_config() + .multi_channel.stereo_detection_threshold, + config_selector_.active_config() + .multi_channel.stereo_detection_timeout_threshold_seconds, + config_selector_.active_config() + .multi_channel.stereo_detection_hysteresis_seconds), output_framer_(num_bands_, num_capture_channels_), capture_blocker_(num_bands_, num_capture_channels_), - render_blocker_(num_bands_, num_render_channels_), render_transfer_queue_( kRenderTransferQueueSizeFrames, std::vector>>( num_bands_, std::vector>( - num_render_channels_, + num_render_input_channels_, std::vector(AudioBuffer::kSplitBandSize, 0.f))), Aec3RenderQueueItemVerifier(num_bands_, - num_render_channels_, + num_render_input_channels_, AudioBuffer::kSplitBandSize)), - block_processor_(std::move(block_processor)), render_queue_output_frame_( num_bands_, std::vector>( - num_render_channels_, + num_render_input_channels_, std::vector(AudioBuffer::kSplitBandSize, 0.f))), render_block_( num_bands_, - std::vector>(num_render_channels_, + std::vector>(num_render_input_channels_, std::vector(kBlockSize, 0.f))), capture_block_( num_bands_, std::vector>(num_capture_channels_, std::vector(kBlockSize, 0.f))), - render_sub_frame_view_( - num_bands_, - std::vector>(num_render_channels_)), capture_sub_frame_view_( num_bands_, std::vector>(num_capture_channels_)) { RTC_DCHECK(ValidFullBandRate(sample_rate_hz_)); - if (config_.delay.fixed_capture_delay_samples > 0) { + if (config_selector_.active_config().delay.fixed_capture_delay_samples > 0) { block_delay_buffer_.reset(new BlockDelayBuffer( num_capture_channels_, num_bands_, AudioBuffer::kSplitBandSize, config_.delay.fixed_capture_delay_samples)); } - render_writer_.reset(new RenderWriter(data_dumper_.get(), config_, - &render_transfer_queue_, num_bands_, - num_render_channels_)); + render_writer_.reset(new RenderWriter( + data_dumper_.get(), config_selector_.active_config(), + &render_transfer_queue_, num_bands_, num_render_input_channels_)); RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000); RTC_DCHECK_GE(kMaxNumBands, num_bands_); - if (config_.filter.export_linear_aec_output) { + if (config_selector_.active_config().filter.export_linear_aec_output) { linear_output_framer_.reset(new BlockFramer(1, num_capture_channels_)); linear_output_block_ = std::make_unique>>>( @@ -752,17 +790,49 @@ EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config, 1, std::vector>(num_capture_channels_)); } + Initialize(); + RTC_LOG(LS_INFO) << "AEC3 created with sample rate: " << sample_rate_hz_ - << " Hz, num render channels: " << num_render_channels_ + << " Hz, num render channels: " << num_render_input_channels_ << ", num capture channels: " << num_capture_channels_; } EchoCanceller3::~EchoCanceller3() = default; +void EchoCanceller3::Initialize() { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + + num_render_channels_to_aec_ = + multichannel_content_detector_.IsProperMultiChannelContentDetected() + ? num_render_input_channels_ + : 1; + + config_selector_.Update( + multichannel_content_detector_.IsProperMultiChannelContentDetected()); + + for (std::vector>& block_band : render_block_) { + block_band.resize(num_render_channels_to_aec_); + for (std::vector& block_channel : block_band) { + block_channel.resize(kBlockSize, 0.0f); + } + } + + render_blocker_.reset( + new FrameBlocker(num_bands_, num_render_channels_to_aec_)); + + block_processor_.reset(BlockProcessor::Create( + config_selector_.active_config(), sample_rate_hz_, + num_render_channels_to_aec_, num_capture_channels_)); + + render_sub_frame_view_ = std::vector>>( + num_bands_, + std::vector>(num_render_channels_to_aec_)); +} + void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) { RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_); - RTC_DCHECK_EQ(render.num_channels(), num_render_channels_); + RTC_DCHECK_EQ(render.num_channels(), num_render_input_channels_); data_dumper_->DumpRaw("aec3_call_order", static_cast(EchoCanceller3ApiCall::kRender)); @@ -810,7 +880,7 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, api_call_metrics_.ReportCaptureCall(); // Optionally delay the capture signal. - if (config_.delay.fixed_capture_delay_samples > 0) { + if (config_selector_.active_config().delay.fixed_capture_delay_samples > 0) { RTC_DCHECK(block_delay_buffer_); block_delay_buffer_->DelaySignal(capture); } @@ -822,22 +892,26 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, EmptyRenderQueue(); - ProcessCaptureFrameContent(linear_output, capture, level_change, - saturated_microphone_signal_, 0, &capture_blocker_, - linear_output_framer_.get(), &output_framer_, - block_processor_.get(), linear_output_block_.get(), - &linear_output_sub_frame_view_, &capture_block_, - &capture_sub_frame_view_); + ProcessCaptureFrameContent( + linear_output, capture, level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, 0, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &linear_output_sub_frame_view_, + &capture_block_, &capture_sub_frame_view_); - ProcessCaptureFrameContent(linear_output, capture, level_change, - saturated_microphone_signal_, 1, &capture_blocker_, - linear_output_framer_.get(), &output_framer_, - block_processor_.get(), linear_output_block_.get(), - &linear_output_sub_frame_view_, &capture_block_, - &capture_sub_frame_view_); + ProcessCaptureFrameContent( + linear_output, capture, level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, 1, &capture_blocker_, + linear_output_framer_.get(), &output_framer_, block_processor_.get(), + linear_output_block_.get(), &linear_output_sub_frame_view_, + &capture_block_, &capture_sub_frame_view_); ProcessRemainingCaptureFrameContent( - level_change, saturated_microphone_signal_, &capture_blocker_, + level_change, + multichannel_content_detector_.IsTemporaryMultiChannelContentDetected(), + saturated_microphone_signal_, &capture_blocker_, linear_output_framer_.get(), &output_framer_, block_processor_.get(), linear_output_block_.get(), &capture_block_); @@ -866,25 +940,28 @@ bool EchoCanceller3::ActiveProcessing() const { return true; } -EchoCanceller3Config EchoCanceller3::CreateDefaultConfig( - size_t num_render_channels, - size_t num_capture_channels) { +EchoCanceller3Config EchoCanceller3::CreateDefaultMultichannelConfig() { EchoCanceller3Config cfg; - if (num_render_channels > 1) { - // Use shorter and more rapidly adapting coarse filter to compensate for - // thge increased number of total filter parameters to adapt. - cfg.filter.coarse.length_blocks = 11; - cfg.filter.coarse.rate = 0.95f; - cfg.filter.coarse_initial.length_blocks = 11; - cfg.filter.coarse_initial.rate = 0.95f; - - // Use more concervative suppressor behavior for non-nearend speech. - cfg.suppressor.normal_tuning.max_dec_factor_lf = 0.35f; - cfg.suppressor.normal_tuning.max_inc_factor = 1.5f; - } + // Use shorter and more rapidly adapting coarse filter to compensate for + // thge increased number of total filter parameters to adapt. + cfg.filter.coarse.length_blocks = 11; + cfg.filter.coarse.rate = 0.95f; + cfg.filter.coarse_initial.length_blocks = 11; + cfg.filter.coarse_initial.rate = 0.95f; + + // Use more concervative suppressor behavior for non-nearend speech. + cfg.suppressor.normal_tuning.max_dec_factor_lf = 0.35f; + cfg.suppressor.normal_tuning.max_inc_factor = 1.5f; return cfg; } +void EchoCanceller3::SetBlockProcessorForTesting( + std::unique_ptr block_processor) { + RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); + RTC_DCHECK(block_processor); + block_processor_ = std::move(block_processor); +} + void EchoCanceller3::EmptyRenderQueue() { RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_); bool frame_to_buffer = @@ -893,16 +970,27 @@ void EchoCanceller3::EmptyRenderQueue() { // Report render call in the metrics. api_call_metrics_.ReportRenderCall(); - BufferRenderFrameContent(&render_queue_output_frame_, 0, &render_blocker_, - block_processor_.get(), &render_block_, - &render_sub_frame_view_); - - BufferRenderFrameContent(&render_queue_output_frame_, 1, &render_blocker_, - block_processor_.get(), &render_block_, - &render_sub_frame_view_); + if (multichannel_content_detector_.UpdateDetection( + render_queue_output_frame_)) { + // Reinitialize the AEC when proper stereo is detected. + Initialize(); + } - BufferRemainingRenderFrameContent(&render_blocker_, block_processor_.get(), - &render_block_); + // Buffer frame content. + BufferRenderFrameContent( + /*proper_downmix_needed=*/multichannel_content_detector_ + .IsTemporaryMultiChannelContentDetected(), + &render_queue_output_frame_, 0, render_blocker_.get(), + block_processor_.get(), &render_block_, &render_sub_frame_view_); + + BufferRenderFrameContent( + /*proper_downmix_needed=*/multichannel_content_detector_ + .IsTemporaryMultiChannelContentDetected(), + &render_queue_output_frame_, 1, render_blocker_.get(), + block_processor_.get(), &render_block_, &render_sub_frame_view_); + + BufferRemainingRenderFrameContent(render_blocker_.get(), + block_processor_.get(), &render_block_); frame_to_buffer = render_transfer_queue_.Remove(&render_queue_output_frame_); diff --git a/modules/audio_processing/aec3/echo_canceller3.h b/modules/audio_processing/aec3/echo_canceller3.h index a4aab4987f..831a7c738a 100644 --- a/modules/audio_processing/aec3/echo_canceller3.h +++ b/modules/audio_processing/aec3/echo_canceller3.h @@ -16,6 +16,7 @@ #include #include +#include "absl/types/optional.h" #include "api/array_view.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_control.h" @@ -23,7 +24,9 @@ #include "modules/audio_processing/aec3/block_delay_buffer.h" #include "modules/audio_processing/aec3/block_framer.h" #include "modules/audio_processing/aec3/block_processor.h" +#include "modules/audio_processing/aec3/config_selector.h" #include "modules/audio_processing/aec3/frame_blocker.h" +#include "modules/audio_processing/aec3/multi_channel_content_detector.h" #include "modules/audio_processing/audio_buffer.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/checks.h" @@ -84,18 +87,15 @@ class Aec3RenderQueueItemVerifier { // AnalyzeRender call which can be called concurrently with the other methods. class EchoCanceller3 : public EchoControl { public: - // Normal c-tor to use. - EchoCanceller3(const EchoCanceller3Config& config, - int sample_rate_hz, - size_t num_render_channels, - size_t num_capture_channels); - // Testing c-tor that is used only for testing purposes. - EchoCanceller3(const EchoCanceller3Config& config, - int sample_rate_hz, - size_t num_render_channels, - size_t num_capture_channels, - std::unique_ptr block_processor); + EchoCanceller3( + const EchoCanceller3Config& config, + const absl::optional& multichannel_config, + int sample_rate_hz, + size_t num_render_channels, + size_t num_capture_channels); + ~EchoCanceller3() override; + EchoCanceller3(const EchoCanceller3&) = delete; EchoCanceller3& operator=(const EchoCanceller3&) = delete; @@ -135,14 +135,39 @@ class EchoCanceller3 : public EchoControl { block_processor_->UpdateEchoLeakageStatus(leakage_detected); } - // Produces a default configuration that is suitable for a certain combination - // of render and capture channels. - static EchoCanceller3Config CreateDefaultConfig(size_t num_render_channels, - size_t num_capture_channels); + // Produces a default configuration for multichannel. + static EchoCanceller3Config CreateDefaultMultichannelConfig(); private: + friend class EchoCanceller3Tester; + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, DetectionOfProperStereo); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + DetectionOfProperStereoUsingThreshold); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + DetectionOfProperStereoUsingHysteresis); + FRIEND_TEST_ALL_PREFIXES(EchoCanceller3, + StereoContentDetectionForMonoSignals); + class RenderWriter; + // (Re-)Initializes the selected subset of the EchoCanceller3 fields, at + // creation as well as during reconfiguration. + void Initialize(); + + // Only for testing. Replaces the internal block processor. + void SetBlockProcessorForTesting( + std::unique_ptr block_processor); + + // Only for testing. Returns whether stereo processing is active. + bool StereoRenderProcessingActiveForTesting() const { + return multichannel_content_detector_.IsProperMultiChannelContentDetected(); + } + + // Only for testing. + const EchoCanceller3Config& GetActiveConfigForTesting() const { + return config_selector_.active_config(); + } + // Empties the render SwapQueue. void EmptyRenderQueue(); @@ -165,13 +190,17 @@ class EchoCanceller3 : public EchoControl { const EchoCanceller3Config config_; const int sample_rate_hz_; const int num_bands_; - const size_t num_render_channels_; + const size_t num_render_input_channels_; + size_t num_render_channels_to_aec_; const size_t num_capture_channels_; + ConfigSelector config_selector_; + MultiChannelContentDetector multichannel_content_detector_; std::unique_ptr linear_output_framer_ RTC_GUARDED_BY(capture_race_checker_); BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_); FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_); - FrameBlocker render_blocker_ RTC_GUARDED_BY(capture_race_checker_); + std::unique_ptr render_blocker_ + RTC_GUARDED_BY(capture_race_checker_); SwapQueue>>, Aec3RenderQueueItemVerifier> render_transfer_queue_; diff --git a/modules/audio_processing/aec3/echo_canceller3_unittest.cc b/modules/audio_processing/aec3/echo_canceller3_unittest.cc index b405e0ca61..9a2df48dde 100644 --- a/modules/audio_processing/aec3/echo_canceller3_unittest.cc +++ b/modules/audio_processing/aec3/echo_canceller3_unittest.cc @@ -178,6 +178,46 @@ class RenderTransportVerificationProcessor : public BlockProcessor { received_render_blocks_; }; +std::string ProduceDebugText(int sample_rate_hz) { + rtc::StringBuilder ss; + ss << "Sample rate: " << sample_rate_hz; + return ss.Release(); +} + +std::string ProduceDebugText(int sample_rate_hz, int variant) { + rtc::StringBuilder ss; + ss << "Sample rate: " << sample_rate_hz << ", variant: " << variant; + return ss.Release(); +} + +void RunAecInStereo(AudioBuffer& buffer, + EchoCanceller3& aec3, + float channel_0_value, + float channel_1_value) { + rtc::ArrayView data_channel_0(&buffer.channels()[0][0], + buffer.num_frames()); + std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value); + rtc::ArrayView data_channel_1(&buffer.channels()[1][0], + buffer.num_frames()); + std::fill(data_channel_1.begin(), data_channel_1.end(), channel_1_value); + aec3.AnalyzeRender(&buffer); + aec3.AnalyzeCapture(&buffer); + aec3.ProcessCapture(&buffer, /*level_change=*/false); +} + +void RunAecInSMono(AudioBuffer& buffer, + EchoCanceller3& aec3, + float channel_0_value) { + rtc::ArrayView data_channel_0(&buffer.channels()[0][0], + buffer.num_frames()); + std::fill(data_channel_0.begin(), data_channel_0.end(), channel_0_value); + aec3.AnalyzeRender(&buffer); + aec3.AnalyzeCapture(&buffer); + aec3.ProcessCapture(&buffer, /*level_change=*/false); +} + +} // namespace + class EchoCanceller3Tester { public: explicit EchoCanceller3Tester(int sample_rate_hz) @@ -206,10 +246,11 @@ class EchoCanceller3Tester { // and that the processor data is properly passed to the EchoCanceller3 // output. void RunCaptureTransportVerificationTest() { - EchoCanceller3 aec3( - EchoCanceller3Config(), sample_rate_hz_, 1, 1, - std::unique_ptr( - new CaptureTransportVerificationProcessor(num_bands_))); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); + aec3.SetBlockProcessorForTesting( + std::make_unique(num_bands_)); for (size_t frame_index = 0; frame_index < kNumFramesToProcess; ++frame_index) { @@ -231,10 +272,11 @@ class EchoCanceller3Tester { // Test method for testing that the render data is properly received by the // block processor. void RunRenderTransportVerificationTest() { - EchoCanceller3 aec3( - EchoCanceller3Config(), sample_rate_hz_, 1, 1, - std::unique_ptr( - new RenderTransportVerificationProcessor(num_bands_))); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); + aec3.SetBlockProcessorForTesting( + std::make_unique(num_bands_)); std::vector> render_input(1); std::vector capture_output; @@ -301,8 +343,10 @@ class EchoCanceller3Tester { break; } - EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1, - std::move(block_processor_mock)); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); + aec3.SetBlockProcessorForTesting(std::move(block_processor_mock)); for (size_t frame_index = 0; frame_index < kNumFramesToProcess; ++frame_index) { @@ -381,8 +425,10 @@ class EchoCanceller3Tester { } break; } - EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1, - std::move(block_processor_mock)); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); + aec3.SetBlockProcessorForTesting(std::move(block_processor_mock)); for (size_t frame_index = 0; frame_index < kNumFramesToProcess; ++frame_index) { @@ -467,8 +513,10 @@ class EchoCanceller3Tester { } break; } - EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1, - std::move(block_processor_mock)); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); + aec3.SetBlockProcessorForTesting(std::move(block_processor_mock)); for (size_t frame_index = 0; frame_index < kNumFramesToProcess; ++frame_index) { for (int k = 0; k < fullband_frame_length_; ++k) { @@ -506,10 +554,10 @@ class EchoCanceller3Tester { // capture and render API calls. void RunRenderSwapQueueVerificationTest() { const EchoCanceller3Config config; - EchoCanceller3 aec3( - config, sample_rate_hz_, 1, 1, - std::unique_ptr( - new RenderTransportVerificationProcessor(num_bands_))); + EchoCanceller3 aec3(config, /*multichannel_config=*/absl::nullopt, + sample_rate_hz_, 1, 1); + aec3.SetBlockProcessorForTesting( + std::make_unique(num_bands_)); std::vector> render_input(1); std::vector capture_output; @@ -555,7 +603,9 @@ class EchoCanceller3Tester { // This test verifies that a buffer overrun in the render swapqueue is // properly reported. void RunRenderPipelineSwapQueueOverrunReturnValueTest() { - EchoCanceller3 aec3(EchoCanceller3Config(), sample_rate_hz_, 1, 1); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, sample_rate_hz_, + 1, 1); constexpr size_t kRenderTransferQueueSize = 30; for (size_t k = 0; k < 2; ++k) { @@ -580,7 +630,9 @@ class EchoCanceller3Tester { // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a // way that the number of bands for the rates are different. const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000; - EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, 1, 1); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, + aec3_sample_rate_hz, 1, 1); PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0); EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), ""); @@ -593,7 +645,9 @@ class EchoCanceller3Tester { // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a // way that the number of bands for the rates are different. const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000; - EchoCanceller3 aec3(EchoCanceller3Config(), aec3_sample_rate_hz, 1, 1); + EchoCanceller3 aec3(EchoCanceller3Config(), + /*multichannel_config=*/absl::nullopt, + aec3_sample_rate_hz, 1, 1); PopulateInputFrame(frame_length_, num_bands_, 0, &capture_buffer_.split_bands_f(0)[0], 100); EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), ""); @@ -618,20 +672,6 @@ class EchoCanceller3Tester { AudioBuffer render_buffer_; }; -std::string ProduceDebugText(int sample_rate_hz) { - rtc::StringBuilder ss; - ss << "Sample rate: " << sample_rate_hz; - return ss.Release(); -} - -std::string ProduceDebugText(int sample_rate_hz, int variant) { - rtc::StringBuilder ss; - ss << "Sample rate: " << sample_rate_hz << ", variant: " << variant; - return ss.Release(); -} - -} // namespace - TEST(EchoCanceller3Buffering, CaptureBitexactness) { for (auto rate : {16000, 32000, 48000}) { SCOPED_TRACE(ProduceDebugText(rate)); @@ -890,6 +930,207 @@ TEST(EchoCanceller3FieldTrials, Aec3UseNearendReverb) { EXPECT_FLOAT_EQ(adjusted_config.ep_strength.nearend_len, 0.8); } +TEST(EchoCanceller3, DetectionOfProperStereo) { + constexpr int kSampleRateHz = 16000; + constexpr int kNumChannels = 2; + AudioBuffer buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/kNumChannels, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/kNumChannels, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/kNumChannels); + + constexpr size_t kNumBlocksForMonoConfig = 1; + constexpr size_t kNumBlocksForSurroundConfig = 2; + EchoCanceller3Config mono_config; + absl::optional multichannel_config; + + mono_config.multi_channel.detect_stereo_content = true; + mono_config.multi_channel.stereo_detection_threshold = 0.0f; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f; + multichannel_config = mono_config; + mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; + multichannel_config->filter.coarse_initial.length_blocks = + kNumBlocksForSurroundConfig; + + EchoCanceller3 aec3(mono_config, multichannel_config, + /*sample_rate_hz=*/kSampleRateHz, + /*num_render_channels=*/kNumChannels, + /*num_capture_input_channels=*/kNumChannels); + + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, 100.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, 101.0f); + EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForSurroundConfig); +} + +TEST(EchoCanceller3, DetectionOfProperStereoUsingThreshold) { + constexpr int kSampleRateHz = 16000; + constexpr int kNumChannels = 2; + AudioBuffer buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/kNumChannels, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/kNumChannels, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/kNumChannels); + + constexpr size_t kNumBlocksForMonoConfig = 1; + constexpr size_t kNumBlocksForSurroundConfig = 2; + EchoCanceller3Config mono_config; + absl::optional multichannel_config; + + constexpr float kStereoDetectionThreshold = 2.0f; + mono_config.multi_channel.detect_stereo_content = true; + mono_config.multi_channel.stereo_detection_threshold = + kStereoDetectionThreshold; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.0f; + multichannel_config = mono_config; + mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; + multichannel_config->filter.coarse_initial.length_blocks = + kNumBlocksForSurroundConfig; + + EchoCanceller3 aec3(mono_config, multichannel_config, + /*sample_rate_hz=*/kSampleRateHz, + /*num_render_channels=*/kNumChannels, + /*num_capture_input_channels=*/kNumChannels); + + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, + 100.0f + kStereoDetectionThreshold - 1.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, + 100.0f + kStereoDetectionThreshold + 10.0f); + EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForSurroundConfig); +} + +TEST(EchoCanceller3, DetectionOfProperStereoUsingHysteresis) { + constexpr int kSampleRateHz = 16000; + constexpr int kNumChannels = 2; + AudioBuffer buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/kNumChannels, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/kNumChannels, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/kNumChannels); + + constexpr size_t kNumBlocksForMonoConfig = 1; + constexpr size_t kNumBlocksForSurroundConfig = 2; + EchoCanceller3Config mono_config; + absl::optional surround_config; + + mono_config.multi_channel.detect_stereo_content = true; + mono_config.multi_channel.stereo_detection_hysteresis_seconds = 0.5f; + surround_config = mono_config; + mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; + surround_config->filter.coarse_initial.length_blocks = + kNumBlocksForSurroundConfig; + + EchoCanceller3 aec3(mono_config, surround_config, + /*sample_rate_hz=*/kSampleRateHz, + /*num_render_channels=*/kNumChannels, + /*num_capture_input_channels=*/kNumChannels); + + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInStereo(buffer, aec3, 100.0f, 100.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + constexpr int kNumFramesPerSecond = 100; + for (int k = 0; + k < static_cast( + kNumFramesPerSecond * + mono_config.multi_channel.stereo_detection_hysteresis_seconds); + ++k) { + RunAecInStereo(buffer, aec3, 100.0f, 101.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + } + + RunAecInStereo(buffer, aec3, 100.0f, 101.0f); + EXPECT_TRUE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForSurroundConfig); +} + +TEST(EchoCanceller3, StereoContentDetectionForMonoSignals) { + constexpr int kSampleRateHz = 16000; + constexpr int kNumChannels = 2; + AudioBuffer buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/kNumChannels, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/kNumChannels, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/kNumChannels); + + constexpr size_t kNumBlocksForMonoConfig = 1; + constexpr size_t kNumBlocksForSurroundConfig = 2; + EchoCanceller3Config mono_config; + absl::optional multichannel_config; + + for (bool detect_stereo_content : {false, true}) { + mono_config.multi_channel.detect_stereo_content = detect_stereo_content; + multichannel_config = mono_config; + mono_config.filter.coarse_initial.length_blocks = kNumBlocksForMonoConfig; + multichannel_config->filter.coarse_initial.length_blocks = + kNumBlocksForSurroundConfig; + + AudioBuffer mono_buffer(/*input_rate=*/kSampleRateHz, + /*input_num_channels=*/1, + /*input_rate=*/kSampleRateHz, + /*buffer_num_channels=*/1, + /*output_rate=*/kSampleRateHz, + /*output_num_channels=*/1); + + EchoCanceller3 aec3(mono_config, multichannel_config, + /*sample_rate_hz=*/kSampleRateHz, + /*num_render_channels=*/1, + /*num_capture_input_channels=*/1); + + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + + RunAecInSMono(mono_buffer, aec3, 100.0f); + EXPECT_FALSE(aec3.StereoRenderProcessingActiveForTesting()); + EXPECT_EQ( + aec3.GetActiveConfigForTesting().filter.coarse_initial.length_blocks, + kNumBlocksForMonoConfig); + } +} + #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) TEST(EchoCanceller3InputCheckDeathTest, WrongCaptureNumBandsCheckVerification) { @@ -902,9 +1143,11 @@ TEST(EchoCanceller3InputCheckDeathTest, WrongCaptureNumBandsCheckVerification) { // Verifiers that the verification for null input to the capture processing api // call works. TEST(EchoCanceller3InputCheckDeathTest, NullCaptureProcessingParameter) { - EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 16000, 1, 1) - .ProcessCapture(nullptr, false), - ""); + EXPECT_DEATH( + EchoCanceller3(EchoCanceller3Config(), + /*multichannel_config_=*/absl::nullopt, 16000, 1, 1) + .ProcessCapture(nullptr, false), + ""); } // Verifies the check for correct sample rate. @@ -912,7 +1155,10 @@ TEST(EchoCanceller3InputCheckDeathTest, NullCaptureProcessingParameter) { // tests on test bots has been fixed. TEST(EchoCanceller3InputCheckDeathTest, DISABLED_WrongSampleRate) { ApmDataDumper data_dumper(0); - EXPECT_DEATH(EchoCanceller3(EchoCanceller3Config(), 8001, 1, 1), ""); + EXPECT_DEATH( + EchoCanceller3(EchoCanceller3Config(), + /*multichannel_config_=*/absl::nullopt, 8001, 1, 1), + ""); } #endif diff --git a/modules/audio_processing/aec3/multi_channel_content_detector.cc b/modules/audio_processing/aec3/multi_channel_content_detector.cc new file mode 100644 index 0000000000..98068964d9 --- /dev/null +++ b/modules/audio_processing/aec3/multi_channel_content_detector.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2022 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/aec3/multi_channel_content_detector.h" + +#include + +#include "rtc_base/checks.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { + +namespace { + +constexpr int kNumFramesPerSecond = 100; + +// Compares the left and right channels in the render `frame` to determine +// whether the signal is a proper stereo signal. To allow for differences +// introduced by hardware drivers, a threshold `detection_threshold` is used for +// the detection. +bool HasStereoContent(const std::vector>>& frame, + float detection_threshold) { + if (frame[0].size() < 2) { + return false; + } + + for (size_t band = 0; band < frame.size(); ++band) { + for (size_t k = 0; k < frame[band][0].size(); ++k) { + if (std::fabs(frame[band][0][k] - frame[band][1][k]) > + detection_threshold) { + return true; + } + } + } + return false; +} + +// In order to avoid logging metrics for very short lifetimes that are unlikely +// to reflect real calls and that may dilute the "real" data, logging is limited +// to lifetimes of at leats 5 seconds. +constexpr int kMinNumberOfFramesRequiredToLogMetrics = 500; + +// Continuous metrics are logged every 10 seconds. +constexpr int kFramesPer10Seconds = 1000; + +} // namespace + +MultiChannelContentDetector::MetricsLogger::MetricsLogger() {} + +MultiChannelContentDetector::MetricsLogger::~MetricsLogger() { + if (frame_counter_ < kMinNumberOfFramesRequiredToLogMetrics) + return; + + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.PersistentMultichannelContentEverDetected", + any_multichannel_content_detected_ ? 1 : 0); +} + +void MultiChannelContentDetector::MetricsLogger::Update( + bool persistent_multichannel_content_detected) { + ++frame_counter_; + if (persistent_multichannel_content_detected) { + any_multichannel_content_detected_ = true; + ++persistent_multichannel_frame_counter_; + } + + if (frame_counter_ < kMinNumberOfFramesRequiredToLogMetrics) + return; + if (frame_counter_ % kFramesPer10Seconds != 0) + return; + const bool mostly_multichannel_last_10_seconds = + (persistent_multichannel_frame_counter_ >= kFramesPer10Seconds / 2); + RTC_HISTOGRAM_BOOLEAN( + "WebRTC.Audio.EchoCanceller.ProcessingPersistentMultichannelContent", + mostly_multichannel_last_10_seconds ? 1 : 0); + + persistent_multichannel_frame_counter_ = 0; +} + +MultiChannelContentDetector::MultiChannelContentDetector( + bool detect_stereo_content, + int num_render_input_channels, + float detection_threshold, + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds) + : detect_stereo_content_(detect_stereo_content), + detection_threshold_(detection_threshold), + detection_timeout_threshold_frames_( + stereo_detection_timeout_threshold_seconds > 0 + ? absl::make_optional(stereo_detection_timeout_threshold_seconds * + kNumFramesPerSecond) + : absl::nullopt), + stereo_detection_hysteresis_frames_(static_cast( + stereo_detection_hysteresis_seconds * kNumFramesPerSecond)), + metrics_logger_((detect_stereo_content && num_render_input_channels > 1) + ? std::make_unique() + : nullptr), + persistent_multichannel_content_detected_( + !detect_stereo_content && num_render_input_channels > 1) {} + +bool MultiChannelContentDetector::UpdateDetection( + const std::vector>>& frame) { + if (!detect_stereo_content_) { + RTC_DCHECK_EQ(frame[0].size() > 1, + persistent_multichannel_content_detected_); + return false; + } + + const bool previous_persistent_multichannel_content_detected = + persistent_multichannel_content_detected_; + const bool stereo_detected_in_frame = + HasStereoContent(frame, detection_threshold_); + + consecutive_frames_with_stereo_ = + stereo_detected_in_frame ? consecutive_frames_with_stereo_ + 1 : 0; + frames_since_stereo_detected_last_ = + stereo_detected_in_frame ? 0 : frames_since_stereo_detected_last_ + 1; + + // Detect persistent multichannel content. + if (consecutive_frames_with_stereo_ > stereo_detection_hysteresis_frames_) { + persistent_multichannel_content_detected_ = true; + } + if (detection_timeout_threshold_frames_.has_value() && + frames_since_stereo_detected_last_ >= + *detection_timeout_threshold_frames_) { + persistent_multichannel_content_detected_ = false; + } + + // Detect temporary multichannel content. + temporary_multichannel_content_detected_ = + persistent_multichannel_content_detected_ ? false + : stereo_detected_in_frame; + + if (metrics_logger_) + metrics_logger_->Update(persistent_multichannel_content_detected_); + + return previous_persistent_multichannel_content_detected != + persistent_multichannel_content_detected_; +} + +} // namespace webrtc diff --git a/modules/audio_processing/aec3/multi_channel_content_detector.h b/modules/audio_processing/aec3/multi_channel_content_detector.h new file mode 100644 index 0000000000..1c6cc1e7a8 --- /dev/null +++ b/modules/audio_processing/aec3/multi_channel_content_detector.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 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_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ + +#include + +#include + +#include "absl/types/optional.h" + +namespace webrtc { + +// Analyzes audio content to determine whether the contained audio is proper +// multichannel, or only upmixed mono. To allow for differences introduced by +// hardware drivers, a threshold `detection_threshold` is used for the +// detection. +// Logs metrics continously and upon destruction. +class MultiChannelContentDetector { + public: + // If |stereo_detection_timeout_threshold_seconds| <= 0, no timeout is + // applied: Once multichannel is detected, the detector remains in that state + // for its lifetime. + MultiChannelContentDetector(bool detect_stereo_content, + int num_render_input_channels, + float detection_threshold, + int stereo_detection_timeout_threshold_seconds, + float stereo_detection_hysteresis_seconds); + + // Compares the left and right channels in the render `frame` to determine + // whether the signal is a proper multichannel signal. Returns a bool + // indicating whether a change in the proper multichannel content was + // detected. + bool UpdateDetection( + const std::vector>>& frame); + + bool IsProperMultiChannelContentDetected() const { + return persistent_multichannel_content_detected_; + } + + bool IsTemporaryMultiChannelContentDetected() const { + return temporary_multichannel_content_detected_; + } + + private: + // Tracks and logs metrics for the amount of multichannel content detected. + class MetricsLogger { + public: + MetricsLogger(); + + // The destructor logs call summary statistics. + ~MetricsLogger(); + + // Updates and logs metrics. + void Update(bool persistent_multichannel_content_detected); + + private: + int frame_counter_ = 0; + + // Counts the number of frames of persistent multichannel audio observed + // during the current metrics collection interval. + int persistent_multichannel_frame_counter_ = 0; + + // Indicates whether persistent multichannel content has ever been detected. + bool any_multichannel_content_detected_ = false; + }; + + const bool detect_stereo_content_; + const float detection_threshold_; + const absl::optional detection_timeout_threshold_frames_; + const int stereo_detection_hysteresis_frames_; + + // Collects and reports metrics on the amount of multichannel content + // detected. Only created if |num_render_input_channels| > 1 and + // |detect_stereo_content_| is true. + const std::unique_ptr metrics_logger_; + + bool persistent_multichannel_content_detected_; + bool temporary_multichannel_content_detected_ = false; + int64_t frames_since_stereo_detected_last_ = 0; + int64_t consecutive_frames_with_stereo_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_MULTI_CHANNEL_CONTENT_DETECTOR_H_ diff --git a/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc b/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc new file mode 100644 index 0000000000..8d38dd0991 --- /dev/null +++ b/modules/audio_processing/aec3/multi_channel_content_detector_unittest.cc @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2022 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/aec3/multi_channel_content_detector.h" + +#include "system_wrappers/include/metrics.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(MultiChannelContentDetector, HandlingOfMono) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/1, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); +} + +TEST(MultiChannelContentDetector, HandlingOfMonoAndDetectionOff) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/false, + /*num_render_input_channels=*/1, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); +} + +TEST(MultiChannelContentDetector, HandlingOfDetectionOff) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/false, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); + + EXPECT_FALSE(mc.UpdateDetection(frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +TEST(MultiChannelContentDetector, InitialDetectionOfStereo) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); +} + +TEST(MultiChannelContentDetector, DetectionWhenFakeStereo) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f); + EXPECT_FALSE(mc.UpdateDetection(frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +TEST(MultiChannelContentDetector, DetectionWhenStereo) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); + EXPECT_TRUE(mc.UpdateDetection(frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +TEST(MultiChannelContentDetector, DetectionWhenStereoAfterAWhile) { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f); + EXPECT_FALSE(mc.UpdateDetection(frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); + + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 101.0f); + + EXPECT_TRUE(mc.UpdateDetection(frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +TEST(MultiChannelContentDetector, DetectionWithStereoBelowThreshold) { + constexpr float kThreshold = 1.0f; + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/kThreshold, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f + kThreshold); + + EXPECT_FALSE(mc.UpdateDetection(frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +TEST(MultiChannelContentDetector, DetectionWithStereoAboveThreshold) { + constexpr float kThreshold = 1.0f; + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/kThreshold, + /*stereo_detection_timeout_threshold_seconds=*/0, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> frame( + 1, std::vector>(2, std::vector(160, 0.0f))); + std::fill(frame[0][0].begin(), frame[0][0].end(), 100.0f); + std::fill(frame[0][1].begin(), frame[0][1].end(), 100.0f + kThreshold + 0.1f); + + EXPECT_TRUE(mc.UpdateDetection(frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + EXPECT_FALSE(mc.UpdateDetection(frame)); +} + +class MultiChannelContentDetectorTimeoutBehavior + : public ::testing::Test, + public ::testing::WithParamInterface> {}; + +INSTANTIATE_TEST_SUITE_P(MultiChannelContentDetector, + MultiChannelContentDetectorTimeoutBehavior, + ::testing::Combine(::testing::Values(false, true), + ::testing::Values(0, 1, 10))); + +TEST_P(MultiChannelContentDetectorTimeoutBehavior, + TimeOutBehaviorForNonTrueStereo) { + constexpr int kNumFramesPerSecond = 100; + const bool detect_stereo_content = std::get<0>(GetParam()); + const int stereo_detection_timeout_threshold_seconds = + std::get<1>(GetParam()); + const int stereo_detection_timeout_threshold_frames = + stereo_detection_timeout_threshold_seconds * kNumFramesPerSecond; + + MultiChannelContentDetector mc(detect_stereo_content, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + stereo_detection_timeout_threshold_seconds, + /*stereo_detection_hysteresis_seconds=*/0.0f); + std::vector>> true_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 101.0f)}}; + + std::vector>> fake_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 100.0f)}}; + + // Pass fake stereo frames and verify the content detection. + for (int k = 0; k < 10; ++k) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + if (detect_stereo_content) { + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + } + + // Pass a true stereo frame and verify that it is properly detected. + if (detect_stereo_content) { + EXPECT_TRUE(mc.UpdateDetection(true_stereo_frame)); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + } + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + + // Pass fake stereo frames until any timeouts are about to occur. + for (int k = 0; k < stereo_detection_timeout_threshold_frames - 1; ++k) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + + // Pass a fake stereo frame and verify that any timeouts properly occur. + if (detect_stereo_content && stereo_detection_timeout_threshold_frames > 0) { + EXPECT_TRUE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + + // Pass fake stereo frames and verify the behavior after any timeout. + for (int k = 0; k < 10; ++k) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + if (detect_stereo_content && + stereo_detection_timeout_threshold_frames > 0) { + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + } +} + +class MultiChannelContentDetectorHysteresisBehavior + : public ::testing::Test, + public ::testing::WithParamInterface> {}; + +INSTANTIATE_TEST_SUITE_P( + MultiChannelContentDetector, + MultiChannelContentDetectorHysteresisBehavior, + ::testing::Combine(::testing::Values(false, true), + ::testing::Values(0.0f, 0.1f, 0.2f))); + +TEST_P(MultiChannelContentDetectorHysteresisBehavior, + PeriodBeforeStereoDetectionIsTriggered) { + constexpr int kNumFramesPerSecond = 100; + const bool detect_stereo_content = std::get<0>(GetParam()); + const int stereo_detection_hysteresis_seconds = std::get<1>(GetParam()); + const int stereo_detection_hysteresis_frames = + stereo_detection_hysteresis_seconds * kNumFramesPerSecond; + + MultiChannelContentDetector mc( + detect_stereo_content, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/0, + stereo_detection_hysteresis_seconds); + std::vector>> true_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 101.0f)}}; + + std::vector>> fake_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 100.0f)}}; + + // Pass fake stereo frames and verify the content detection. + for (int k = 0; k < 10; ++k) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + if (detect_stereo_content) { + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + } else { + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + } + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } + + // Pass a two true stereo frames and verify that they are properly detected. + ASSERT_TRUE(stereo_detection_hysteresis_frames > 2 || + stereo_detection_hysteresis_frames == 0); + for (int k = 0; k < 2; ++k) { + if (detect_stereo_content) { + if (stereo_detection_hysteresis_seconds == 0.0f) { + if (k == 0) { + EXPECT_TRUE(mc.UpdateDetection(true_stereo_frame)); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + } + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsTemporaryMultiChannelContentDetected()); + } + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } + } + + if (stereo_detection_hysteresis_seconds == 0.0f) { + return; + } + + // Pass true stereo frames until any timeouts are about to occur. + for (int k = 0; k < stereo_detection_hysteresis_frames - 3; ++k) { + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_FALSE(mc.IsProperMultiChannelContentDetected()); + EXPECT_TRUE(mc.IsTemporaryMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } + } + + // Pass a true stereo frame and verify that it is properly detected. + if (detect_stereo_content) { + EXPECT_TRUE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } + + // Pass an additional true stereo frame and verify that it is properly + // detected. + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(true_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } + + // Pass a fake stereo frame and verify that it is properly detected. + if (detect_stereo_content) { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } else { + EXPECT_FALSE(mc.UpdateDetection(fake_stereo_frame)); + EXPECT_TRUE(mc.IsProperMultiChannelContentDetected()); + EXPECT_FALSE(mc.IsTemporaryMultiChannelContentDetected()); + } +} + +class MultiChannelContentDetectorMetricsDisabled + : public ::testing::Test, + public ::testing::WithParamInterface> {}; + +INSTANTIATE_TEST_SUITE_P( + /*no prefix*/, + MultiChannelContentDetectorMetricsDisabled, + ::testing::Values(std::tuple(false, 2), + std::tuple(true, 1))); + +// Test that no metrics are logged when they are clearly uninteresting and would +// dilute relevant data: when the reference audio is single channel, or when +// dynamic detection is disabled. +TEST_P(MultiChannelContentDetectorMetricsDisabled, ReportsNoMetrics) { + metrics::Reset(); + constexpr int kNumFramesPerSecond = 100; + const bool detect_stereo_content = std::get<0>(GetParam()); + const int channel_count = std::get<1>(GetParam()); + std::vector>> audio_frame = { + std::vector>(channel_count, + std::vector(160, 100.0f))}; + { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/detect_stereo_content, + /*num_render_input_channels=*/channel_count, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/1, + /*stereo_detection_hysteresis_seconds=*/0.0f); + for (int k = 0; k < 20 * kNumFramesPerSecond; ++k) { + mc.UpdateDetection(audio_frame); + } + } + EXPECT_METRIC_EQ( + 0, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "ProcessingPersistentMultichannelContent")); + EXPECT_METRIC_EQ( + 0, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "PersistentMultichannelContentEverDetected")); +} + +// Tests that after 3 seconds, no metrics are reported. +TEST(MultiChannelContentDetectorMetrics, ReportsNoMetricsForShortLifetime) { + metrics::Reset(); + constexpr int kNumFramesPerSecond = 100; + constexpr int kTooFewFramesToLogMetrics = 3 * kNumFramesPerSecond; + std::vector>> audio_frame = { + std::vector>(2, std::vector(160, 100.0f))}; + { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/1, + /*stereo_detection_hysteresis_seconds=*/0.0f); + for (int k = 0; k < kTooFewFramesToLogMetrics; ++k) { + mc.UpdateDetection(audio_frame); + } + } + EXPECT_METRIC_EQ( + 0, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "ProcessingPersistentMultichannelContent")); + EXPECT_METRIC_EQ( + 0, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "PersistentMultichannelContentEverDetected")); +} + +// Tests that after 25 seconds, metrics are reported. +TEST(MultiChannelContentDetectorMetrics, ReportsMetrics) { + metrics::Reset(); + constexpr int kNumFramesPerSecond = 100; + std::vector>> true_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 101.0f)}}; + std::vector>> fake_stereo_frame = { + {std::vector(160, 100.0f), std::vector(160, 100.0f)}}; + { + MultiChannelContentDetector mc( + /*detect_stereo_content=*/true, + /*num_render_input_channels=*/2, + /*detection_threshold=*/0.0f, + /*stereo_detection_timeout_threshold_seconds=*/1, + /*stereo_detection_hysteresis_seconds=*/0.0f); + for (int k = 0; k < 10 * kNumFramesPerSecond; ++k) { + mc.UpdateDetection(true_stereo_frame); + } + for (int k = 0; k < 15 * kNumFramesPerSecond; ++k) { + mc.UpdateDetection(fake_stereo_frame); + } + } + // After 10 seconds of true stereo and the remainder fake stereo, we expect + // one lifetime metric sample (multichannel detected) and two periodic samples + // (one multichannel, one mono). + + // Check lifetime metric. + EXPECT_METRIC_EQ( + 1, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "PersistentMultichannelContentEverDetected")); + EXPECT_METRIC_EQ( + 1, metrics::NumEvents("WebRTC.Audio.EchoCanceller." + "PersistentMultichannelContentEverDetected", 1)); + + // Check periodic metric. + EXPECT_METRIC_EQ( + 2, metrics::NumSamples("WebRTC.Audio.EchoCanceller." + "ProcessingPersistentMultichannelContent")); + EXPECT_METRIC_EQ( + 1, metrics::NumEvents("WebRTC.Audio.EchoCanceller." + "ProcessingPersistentMultichannelContent", 0)); + EXPECT_METRIC_EQ( + 1, metrics::NumEvents("WebRTC.Audio.EchoCanceller." + "ProcessingPersistentMultichannelContent", 1)); +} + +} // namespace webrtc diff --git a/modules/audio_processing/aec_dump/BUILD.gn b/modules/audio_processing/aec_dump/BUILD.gn index 9887f7dcf0..78d9112001 100644 --- a/modules/audio_processing/aec_dump/BUILD.gn +++ b/modules/audio_processing/aec_dump/BUILD.gn @@ -14,7 +14,6 @@ rtc_source_set("aec_dump") { deps = [ "..:aec_dump_interface", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:file_wrapper", "../../../rtc_base/system:rtc_export", ] @@ -46,7 +45,6 @@ if (rtc_include_tests) { "..:api", "..:audioproc_test_utils", "../", - "../../../rtc_base:rtc_base_approved", "//testing/gtest", ] } @@ -70,8 +68,11 @@ if (rtc_enable_protobuf) { "../../../api/task_queue", "../../../rtc_base:checks", "../../../rtc_base:ignore_wundef", + "../../../rtc_base:logging", + "../../../rtc_base:macromagic", "../../../rtc_base:protobuf_utils", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:race_checker", + "../../../rtc_base:rtc_event", "../../../rtc_base:rtc_task_queue", "../../../rtc_base/system:file_wrapper", "../../../system_wrappers", diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc index 160583e7c3..1b87465f87 100644 --- a/modules/audio_processing/aec_dump/aec_dump_impl.cc +++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc @@ -255,8 +255,8 @@ std::unique_ptr AecDumpFactory::Create(webrtc::FileWrapper file, std::unique_ptr AecDumpFactory::Create(std::string file_name, int64_t max_log_size_bytes, rtc::TaskQueue* worker_queue) { - return Create(FileWrapper::OpenWriteOnly(file_name.c_str()), - max_log_size_bytes, worker_queue); + return Create(FileWrapper::OpenWriteOnly(file_name), max_log_size_bytes, + worker_queue); } std::unique_ptr AecDumpFactory::Create(FILE* handle, diff --git a/modules/audio_processing/aecm/BUILD.gn b/modules/audio_processing/aecm/BUILD.gn index 61e9affdea..80f2901049 100644 --- a/modules/audio_processing/aecm/BUILD.gn +++ b/modules/audio_processing/aecm/BUILD.gn @@ -19,7 +19,7 @@ rtc_library("aecm_core") { deps = [ "../../../common_audio:common_audio_c", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base:sanitizer", "../../../system_wrappers", "../utility:legacy_delay_estimator", diff --git a/modules/audio_processing/agc/BUILD.gn b/modules/audio_processing/agc/BUILD.gn index eef1b77560..7b8f0fd82e 100644 --- a/modules/audio_processing/agc/BUILD.gn +++ b/modules/audio_processing/agc/BUILD.gn @@ -30,10 +30,10 @@ rtc_library("agc") { "../../../api:array_view", "../../../common_audio", "../../../common_audio:common_audio_c", + "../../../rtc_base:atomicops", "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", "../../../rtc_base:logging", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", "../../../system_wrappers:field_trial", "../../../system_wrappers:metrics", @@ -94,7 +94,6 @@ rtc_library("clipping_predictor_level_buffer") { deps = [ "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -136,7 +135,6 @@ rtc_library("legacy_agc") { "../../../common_audio:common_audio_c", "../../../common_audio/third_party/ooura:fft_size_256", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../../../system_wrappers", ] @@ -178,7 +176,7 @@ if (rtc_include_tests) { "..:mocks", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:random", "../../../rtc_base:safe_conversions", "../../../rtc_base:stringutils", "../../../system_wrappers:metrics", diff --git a/modules/audio_processing/agc/agc_manager_direct.cc b/modules/audio_processing/agc/agc_manager_direct.cc index 8bce7690a3..fb54cf52f6 100644 --- a/modules/audio_processing/agc/agc_manager_direct.cc +++ b/modules/audio_processing/agc/agc_manager_direct.cc @@ -52,7 +52,7 @@ constexpr int kSurplusCompressionGain = 6; // History size for the clipping predictor evaluator (unit: number of 10 ms // frames). -constexpr int kClippingPredictorEvaluatorHistorySize = 32; +constexpr int kClippingPredictorEvaluatorHistorySize = 500; using ClippingPredictorConfig = AudioProcessing::Config::GainController1:: AnalogGainController::ClippingPredictor; @@ -609,9 +609,14 @@ void AgcManagerDirect::AnalyzePreProcess(const float* const* audio, } } // Clipping prediction evaluation. + // `clipping_detected` is not used to evaluate the clipping predictor + // since for this purpose a single clipping sample counts as clipping. + const bool one_or_more_clipped_samples = + clipped_ratio >= (1.0f / samples_per_channel); absl::optional prediction_interval = - clipping_predictor_evaluator_.Observe(clipping_detected, - clipping_predicted); + clipping_predictor_evaluator_.Observe( + /*clipping_detected=*/one_or_more_clipped_samples, + clipping_predicted); if (prediction_interval.has_value()) { RTC_HISTOGRAM_COUNTS_LINEAR( "WebRTC.Audio.Agc.ClippingPredictor.PredictionInterval", diff --git a/modules/audio_processing/agc/agc_manager_direct_unittest.cc b/modules/audio_processing/agc/agc_manager_direct_unittest.cc index d727449229..5c9b383f78 100644 --- a/modules/audio_processing/agc/agc_manager_direct_unittest.cc +++ b/modules/audio_processing/agc/agc_manager_direct_unittest.cc @@ -128,13 +128,15 @@ void CallPreProcessAudioBuffer(int num_calls, } } -std::string GetAgcMinMicLevelExperimentFieldTrial(int enabled_value) { +std::string GetAgcMinMicLevelExperimentFieldTrial( + int enabled_value, + const std::string& suffix = "") { RTC_DCHECK_GE(enabled_value, 0); RTC_DCHECK_LE(enabled_value, 255); char field_trial_buffer[64]; rtc::SimpleStringBuilder builder(field_trial_buffer); builder << "WebRTC-Audio-AgcMinMicLevelExperiment/Enabled-" << enabled_value - << "/"; + << suffix << "/"; return builder.str(); } @@ -883,13 +885,16 @@ TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperiment) { } TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentDisabled) { - test::ScopedFieldTrials field_trial( - "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled/"); - std::unique_ptr manager = - CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, - kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevel); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + for (const std::string& field_trial_suffix : {"", "_20220210"}) { + test::ScopedFieldTrials field_trial( + "WebRTC-Audio-AgcMinMicLevelExperiment/Disabled" + field_trial_suffix + + "/"); + std::unique_ptr manager = + CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, + kClippedRatioThreshold, kClippedWaitFrames); + 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 @@ -921,13 +926,16 @@ TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentOutOfRangeBelow) { // changed. TEST(AgcManagerDirectStandaloneTest, AgcMinMicLevelExperimentEnabled50) { constexpr int kMinMicLevelOverride = 50; - test::ScopedFieldTrials field_trial( - GetAgcMinMicLevelExperimentFieldTrial(kMinMicLevelOverride)); - std::unique_ptr manager = - CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, - kClippedRatioThreshold, kClippedWaitFrames); - EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevelOverride); - EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + for (const std::string& field_trial_suffix : {"", "_20220210"}) { + SCOPED_TRACE(field_trial_suffix); + test::ScopedFieldTrials field_trial(GetAgcMinMicLevelExperimentFieldTrial( + kMinMicLevelOverride, field_trial_suffix)); + std::unique_ptr manager = + CreateAgcManagerDirect(kInitialVolume, kClippedLevelStep, + kClippedRatioThreshold, kClippedWaitFrames); + EXPECT_EQ(manager->channel_agcs_[0]->min_mic_level(), kMinMicLevelOverride); + EXPECT_EQ(manager->channel_agcs_[0]->startup_min_level(), kInitialVolume); + } } // Checks that, when the "WebRTC-Audio-AgcMinMicLevelExperiment" field trial is diff --git a/modules/audio_processing/agc/loudness_histogram_unittest.cc b/modules/audio_processing/agc/loudness_histogram_unittest.cc index 30ea5d326c..af193ee8f1 100644 --- a/modules/audio_processing/agc/loudness_histogram_unittest.cc +++ b/modules/audio_processing/agc/loudness_histogram_unittest.cc @@ -63,14 +63,12 @@ void LoudnessHistogramTest::RunTest(bool enable_circular_buff, InputOutput io; int num_updates = 0; - int num_reset = 0; while (fread(&io, sizeof(InputOutput), 1, in_file) == 1) { if (io.rms < 0) { // We have to reset. hist_->Reset(); TestClean(); num_updates = 0; - num_reset++; // Read the next chunk of input. if (fread(&io, sizeof(InputOutput), 1, in_file) != 1) break; diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn index e12252806b..4e40736d52 100644 --- a/modules/audio_processing/agc2/BUILD.gn +++ b/modules/audio_processing/agc2/BUILD.gn @@ -49,7 +49,6 @@ rtc_library("adaptive_digital") { "../../../common_audio", "../../../rtc_base:checks", "../../../rtc_base:logging", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_compare", "../../../rtc_base:safe_minmax", "../../../system_wrappers:metrics", @@ -66,7 +65,7 @@ rtc_library("biquad_filter") { ] deps = [ "../../../api:array_view", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:macromagic", ] } @@ -100,7 +99,6 @@ rtc_library("fixed_digital") { "../../../common_audio", "../../../rtc_base:checks", "../../../rtc_base:gtest_prod", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", "../../../system_wrappers:metrics", @@ -214,7 +212,6 @@ rtc_library("adaptive_digital_unittests") { "../../../common_audio", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", ] } @@ -253,7 +250,6 @@ rtc_library("fixed_digital_unittests") { "../../../common_audio", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../system_wrappers:metrics", ] } @@ -272,7 +268,6 @@ rtc_library("noise_estimator_unittests") { "../../../api:function_view", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", ] } @@ -305,6 +300,6 @@ rtc_library("test_utils") { deps = [ "..:audio_frame_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:random", ] } diff --git a/modules/audio_processing/agc2/rnn_vad/BUILD.gn b/modules/audio_processing/agc2/rnn_vad/BUILD.gn index f0d7093783..9d62e2ff51 100644 --- a/modules/audio_processing/agc2/rnn_vad/BUILD.gn +++ b/modules/audio_processing/agc2/rnn_vad/BUILD.gn @@ -322,7 +322,7 @@ if (rtc_include_tests) { "..:cpu_features", "../../../../api:array_view", "../../../../common_audio", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", "../../../../rtc_base:safe_compare", "../../../../test:test_support", "//third_party/abseil-cpp/absl/flags:flag", diff --git a/modules/audio_processing/audio_buffer.cc b/modules/audio_processing/audio_buffer.cc index ff6636df87..3dbe1fe072 100644 --- a/modules/audio_processing/audio_buffer.cc +++ b/modules/audio_processing/audio_buffer.cc @@ -45,22 +45,11 @@ AudioBuffer::AudioBuffer(size_t input_rate, size_t buffer_num_channels, size_t output_rate, size_t output_num_channels) - : AudioBuffer(static_cast(input_rate) / 100, - input_num_channels, - static_cast(buffer_rate) / 100, - buffer_num_channels, - static_cast(output_rate) / 100) {} - -AudioBuffer::AudioBuffer(size_t input_num_frames, - size_t input_num_channels, - size_t buffer_num_frames, - size_t buffer_num_channels, - size_t output_num_frames) - : input_num_frames_(input_num_frames), + : input_num_frames_(static_cast(input_rate) / 100), input_num_channels_(input_num_channels), - buffer_num_frames_(buffer_num_frames), + buffer_num_frames_(static_cast(buffer_rate) / 100), buffer_num_channels_(buffer_num_channels), - output_num_frames_(output_num_frames), + output_num_frames_(static_cast(output_rate) / 100), output_num_channels_(0), num_channels_(buffer_num_channels), num_bands_(NumBandsFromFramesPerChannel(buffer_num_frames_)), diff --git a/modules/audio_processing/audio_buffer.h b/modules/audio_processing/audio_buffer.h index ab0af4493c..d866b8bce5 100644 --- a/modules/audio_processing/audio_buffer.h +++ b/modules/audio_processing/audio_buffer.h @@ -40,12 +40,6 @@ class AudioBuffer { size_t output_rate, size_t output_num_channels); - // The constructor below will be deprecated. - AudioBuffer(size_t input_num_frames, - size_t input_num_channels, - size_t buffer_num_frames, - size_t buffer_num_channels, - size_t output_num_frames); virtual ~AudioBuffer(); AudioBuffer(const AudioBuffer&) = delete; diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc index 9a1aaee821..a36fc4a1bd 100644 --- a/modules/audio_processing/audio_processing_impl.cc +++ b/modules/audio_processing/audio_processing_impl.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/match.h" #include "absl/types/optional.h" #include "api/array_view.h" #include "api/audio/audio_frame.h" @@ -67,6 +68,29 @@ bool UseSetupSpecificDefaultAec3Congfig() { "WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch"); } +// If the "WebRTC-Audio-TransientSuppressorVadMode" field trial is unspecified, +// returns `TransientSuppressor::VadMode::kDefault`, otherwise parses the field +// trial and returns the specified mode: +// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default returns `kDefault`; +// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad returns `kRnnVad`; +// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-NoVad returns `kNoVad`. +TransientSuppressor::VadMode GetTransientSuppressorVadMode() { + constexpr char kFieldTrial[] = "WebRTC-Audio-TransientSuppressorVadMode"; + std::string full_name = webrtc::field_trial::FindFullName(kFieldTrial); + if (full_name.empty() || absl::EndsWith(full_name, "-Default")) { + return TransientSuppressor::VadMode::kDefault; + } + if (absl::EndsWith(full_name, "-RnnVad")) { + return TransientSuppressor::VadMode::kRnnVad; + } + if (absl::EndsWith(full_name, "-NoVad")) { + return TransientSuppressor::VadMode::kNoVad; + } + // Fallback to default. + RTC_LOG(LS_WARNING) << "Invalid parameter for " << kFieldTrial; + return TransientSuppressor::VadMode::kDefault; +} + // Identify the native processing rate that best handles a sample rate. int SuitableProcessRate(int minimum_rate, int max_splitting_rate, @@ -241,6 +265,7 @@ AudioProcessingImpl::AudioProcessingImpl( UseSetupSpecificDefaultAec3Congfig()), use_denormal_disabler_( !field_trial::IsEnabled("WebRTC-ApmDenormalDisablerKillSwitch")), + transient_suppressor_vad_mode_(GetTransientSuppressorVadMode()), capture_runtime_settings_(RuntimeSettingQueueSize()), render_runtime_settings_(RuntimeSettingQueueSize()), capture_runtime_settings_enqueuer_(&capture_runtime_settings_), @@ -1244,14 +1269,21 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() { 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; - + float voice_probability = 1.0f; + switch (transient_suppressor_vad_mode_) { + case TransientSuppressor::VadMode::kDefault: + if (submodules_.agc_manager) { + voice_probability = submodules_.agc_manager->voice_probability(); + } + break; + case TransientSuppressor::VadMode::kRnnVad: + // TODO(bugs.webrtc.org/13663): Use RNN VAD. + break; + case TransientSuppressor::VadMode::kNoVad: + // The transient suppressor will ignore `voice_probability`. + break; + } submodules_.transient_suppressor->Suppress( capture_buffer->channels()[0], capture_buffer->num_frames(), capture_buffer->num_channels(), @@ -1672,16 +1704,18 @@ void AudioProcessingImpl::InitializeTransientSuppressor() { !constants_.transient_suppressor_forced_off) { // Attempt to create a transient suppressor, if one is not already created. if (!submodules_.transient_suppressor) { - submodules_.transient_suppressor = - CreateTransientSuppressor(submodule_creation_overrides_); - } - if (submodules_.transient_suppressor) { - submodules_.transient_suppressor->Initialize( + submodules_.transient_suppressor = CreateTransientSuppressor( + submodule_creation_overrides_, transient_suppressor_vad_mode_, proc_fullband_sample_rate_hz(), capture_nonlocked_.split_rate, num_proc_channels()); + if (!submodules_.transient_suppressor) { + RTC_LOG(LS_WARNING) + << "No transient suppressor created (probably disabled)"; + } } else { - RTC_LOG(LS_WARNING) - << "No transient suppressor created (probably disabled)"; + submodules_.transient_suppressor->Initialize( + proc_fullband_sample_rate_hz(), capture_nonlocked_.split_rate, + num_proc_channels()); } } else { submodules_.transient_suppressor.reset(); @@ -1726,14 +1760,14 @@ void AudioProcessingImpl::InitializeEchoController() { proc_sample_rate_hz(), num_reverse_channels(), num_proc_channels()); RTC_DCHECK(submodules_.echo_controller); } else { - EchoCanceller3Config config = - use_setup_specific_default_aec3_config_ - ? EchoCanceller3::CreateDefaultConfig(num_reverse_channels(), - num_proc_channels()) - : EchoCanceller3Config(); + EchoCanceller3Config config; + absl::optional multichannel_config; + if (use_setup_specific_default_aec3_config_) { + multichannel_config = EchoCanceller3::CreateDefaultMultichannelConfig(); + } submodules_.echo_controller = std::make_unique( - config, proc_sample_rate_hz(), num_reverse_channels(), - num_proc_channels()); + config, multichannel_config, proc_sample_rate_hz(), + num_reverse_channels(), num_proc_channels()); } // Setup the storage for returning the linear AEC output. diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h index 344b8c5959..0fc6c9f2ef 100644 --- a/modules/audio_processing/audio_processing_impl.h +++ b/modules/audio_processing/audio_processing_impl.h @@ -185,6 +185,8 @@ class AudioProcessingImpl : public AudioProcessing { const bool use_denormal_disabler_; + const TransientSuppressor::VadMode transient_suppressor_vad_mode_; + SwapQueue capture_runtime_settings_; SwapQueue render_runtime_settings_; diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc index b21a0227c5..e26548a905 100644 --- a/modules/audio_processing/audio_processing_unittest.cc +++ b/modules/audio_processing/audio_processing_unittest.cc @@ -1600,7 +1600,7 @@ TEST_F(ApmTest, DebugDumpFromFileHandle) { const std::string filename = test::TempFilename(test::OutputPath(), "debug_aec"); - FileWrapper f = FileWrapper::OpenWriteOnly(filename.c_str()); + FileWrapper f = FileWrapper::OpenWriteOnly(filename); ASSERT_TRUE(f.is_open()); #ifdef WEBRTC_AUDIOPROC_DEBUG_DUMP diff --git a/modules/audio_processing/include/aec_dump.cc b/modules/audio_processing/include/aec_dump.cc index 67809d0dcb..8f788cb802 100644 --- a/modules/audio_processing/include/aec_dump.cc +++ b/modules/audio_processing/include/aec_dump.cc @@ -17,7 +17,7 @@ InternalAPMConfig::InternalAPMConfig(InternalAPMConfig&&) = default; InternalAPMConfig& InternalAPMConfig::operator=(const InternalAPMConfig&) = default; -bool InternalAPMConfig::operator==(const InternalAPMConfig& other) { +bool InternalAPMConfig::operator==(const InternalAPMConfig& other) const { return aec_enabled == other.aec_enabled && aec_delay_agnostic_enabled == other.aec_delay_agnostic_enabled && aec_drift_compensation_enabled == diff --git a/modules/audio_processing/include/aec_dump.h b/modules/audio_processing/include/aec_dump.h index a7769d9973..07477d2f82 100644 --- a/modules/audio_processing/include/aec_dump.h +++ b/modules/audio_processing/include/aec_dump.h @@ -31,7 +31,7 @@ struct InternalAPMConfig { InternalAPMConfig& operator=(const InternalAPMConfig&); InternalAPMConfig& operator=(InternalAPMConfig&&) = delete; - bool operator==(const InternalAPMConfig& other); + bool operator==(const InternalAPMConfig& other) const; bool aec_enabled = false; bool aec_delay_agnostic_enabled = false; diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h index 9d6824c038..1088f9b46d 100644 --- a/modules/audio_processing/include/audio_processing.h +++ b/modules/audio_processing/include/audio_processing.h @@ -369,12 +369,6 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface { } adaptive_digital; } gain_controller2; - // TODO(bugs.webrtc.org/11539): Deprecated. Delete this flag. Replaced by - // injectable submodule. - struct ResidualEchoDetector { - bool enabled = false; - } residual_echo_detector; - std::string ToString() const; }; diff --git a/modules/audio_processing/ns/BUILD.gn b/modules/audio_processing/ns/BUILD.gn index eb99c775a9..d818e23f3c 100644 --- a/modules/audio_processing/ns/BUILD.gn +++ b/modules/audio_processing/ns/BUILD.gn @@ -57,7 +57,6 @@ rtc_static_library("ns") { "../../../common_audio/third_party/ooura:fft_size_128", "../../../common_audio/third_party/ooura:fft_size_256", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", "../../../rtc_base/system:arch", "../../../system_wrappers", @@ -83,8 +82,8 @@ if (rtc_include_tests) { "..:high_pass_filter", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base:safe_minmax", + "../../../rtc_base:stringutils", "../../../rtc_base/system:arch", "../../../system_wrappers", "../../../test:test_support", diff --git a/modules/audio_processing/optionally_built_submodule_creators.cc b/modules/audio_processing/optionally_built_submodule_creators.cc index 62a1632566..cea5c837dc 100644 --- a/modules/audio_processing/optionally_built_submodule_creators.cc +++ b/modules/audio_processing/optionally_built_submodule_creators.cc @@ -17,14 +17,19 @@ namespace webrtc { std::unique_ptr CreateTransientSuppressor( - const ApmSubmoduleCreationOverrides& overrides) { + const ApmSubmoduleCreationOverrides& overrides, + TransientSuppressor::VadMode vad_mode, + int sample_rate_hz, + int detection_rate_hz, + int num_channels) { #ifdef WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR return nullptr; #else if (overrides.transient_suppression) { return nullptr; } - return std::make_unique(); + return std::make_unique( + vad_mode, sample_rate_hz, detection_rate_hz, num_channels); #endif } diff --git a/modules/audio_processing/optionally_built_submodule_creators.h b/modules/audio_processing/optionally_built_submodule_creators.h index 7de337b277..1be2743986 100644 --- a/modules/audio_processing/optionally_built_submodule_creators.h +++ b/modules/audio_processing/optionally_built_submodule_creators.h @@ -31,7 +31,11 @@ struct ApmSubmoduleCreationOverrides { // * WEBRTC_EXCLUDE_TRANSIENT_SUPPRESSOR is defined // * The corresponding override in `overrides` is enabled. std::unique_ptr CreateTransientSuppressor( - const ApmSubmoduleCreationOverrides& overrides); + const ApmSubmoduleCreationOverrides& overrides, + TransientSuppressor::VadMode vad_mode, + int sample_rate_hz, + int detection_rate_hz, + int num_channels); } // namespace webrtc diff --git a/modules/audio_processing/rms_level.cc b/modules/audio_processing/rms_level.cc index 6992a15194..b0a45cb403 100644 --- a/modules/audio_processing/rms_level.cc +++ b/modules/audio_processing/rms_level.cc @@ -101,8 +101,18 @@ void RmsLevel::AnalyzeMuted(size_t length) { } int RmsLevel::Average() { - int rms = (sample_count_ == 0) ? RmsLevel::kMinLevelDb - : ComputeRms(sum_square_ / sample_count_); + const bool have_samples = (sample_count_ != 0); + int rms = have_samples ? ComputeRms(sum_square_ / sample_count_) + : RmsLevel::kMinLevelDb; + + // To ensure that kMinLevelDb represents digital silence (muted audio + // sources) we'll check here if the sum_square is actually 0. If it's not + // we'll bump up the return value to `kInaudibleButNotMuted`. + // https://datatracker.ietf.org/doc/html/rfc6464 + if (have_samples && rms == RmsLevel::kMinLevelDb && sum_square_ != 0.0f) { + rms = kInaudibleButNotMuted; + } + Reset(); return rms; } diff --git a/modules/audio_processing/rms_level.h b/modules/audio_processing/rms_level.h index 4955d1b308..fbece19ecd 100644 --- a/modules/audio_processing/rms_level.h +++ b/modules/audio_processing/rms_level.h @@ -34,7 +34,7 @@ class RmsLevel { int peak; }; - enum : int { kMinLevelDb = 127 }; + enum : int { kMinLevelDb = 127, kInaudibleButNotMuted = 126 }; RmsLevel(); ~RmsLevel(); diff --git a/modules/audio_processing/rms_level_unittest.cc b/modules/audio_processing/rms_level_unittest.cc index daf355d074..4cbad461e7 100644 --- a/modules/audio_processing/rms_level_unittest.cc +++ b/modules/audio_processing/rms_level_unittest.cc @@ -151,6 +151,18 @@ TEST(RmsLevelTest, ProcessMuted) { EXPECT_EQ(6, level->Average()); // Average RMS halved due to the silence. } +// Digital silence must yield 127 and anything else should yield 126 or lower. +TEST(RmsLevelTest, OnlyDigitalSilenceIs127) { + std::vector test_buffer(kSampleRateHz, 0); + auto level = RunTest(test_buffer); + EXPECT_EQ(127, level->Average()); + // Change one sample to something other than 0 to make the buffer not strictly + // represent digital silence. + test_buffer[0] = 1; + level = RunTest(test_buffer); + EXPECT_LT(level->Average(), 127); +} + // Inserts 1 second of half-scale sinusoid, follwed by 10 ms of full-scale, and // finally 1 second of half-scale again. Expect the average to be -9 dBFS due // to the vast majority of the signal being half-scale, and the peak to be diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc index 621c3a43d0..ec35dd345c 100644 --- a/modules/audio_processing/test/aec_dump_based_simulator.cc +++ b/modules/audio_processing/test/aec_dump_based_simulator.cc @@ -164,15 +164,14 @@ void AecDumpBasedSimulator::PrepareProcessStreamCall( } } - if (!settings_.use_ts || *settings_.use_ts == 1) { - // Transient suppressor activated (1) or not specified. + if (settings_.override_key_pressed.has_value()) { + // Key pressed state overridden. + ap_->set_stream_key_pressed(*settings_.override_key_pressed); + } else { + // Set the recorded key pressed state. if (msg.has_keypress()) { ap_->set_stream_key_pressed(msg.keypress()); } - } else { - // Transient suppressor deactivated (0) or activated with continuous key - // events (2). - ap_->set_stream_key_pressed(*settings_.use_ts == 2); } // Level is always logged in AEC dumps. diff --git a/modules/audio_processing/test/audio_processing_simulator.cc b/modules/audio_processing/test/audio_processing_simulator.cc index 4915648fa9..5923fc34f6 100644 --- a/modules/audio_processing/test/audio_processing_simulator.cc +++ b/modules/audio_processing/test/audio_processing_simulator.cc @@ -565,9 +565,9 @@ void AudioProcessingSimulator::ConfigureAudioProcessor() { apm_config.gain_controller1.analog_gain_controller.enabled = *settings_.use_analog_agc; } - if (settings_.analog_agc_disable_digital_adaptive) { + if (settings_.analog_agc_use_digital_adaptive_controller) { apm_config.gain_controller1.analog_gain_controller.enable_digital_adaptive = - *settings_.analog_agc_disable_digital_adaptive; + *settings_.analog_agc_use_digital_adaptive_controller; } if (settings_.maximum_internal_processing_rate) { diff --git a/modules/audio_processing/test/audio_processing_simulator.h b/modules/audio_processing/test/audio_processing_simulator.h index af76d7e1c9..b63bc12d6f 100644 --- a/modules/audio_processing/test/audio_processing_simulator.h +++ b/modules/audio_processing/test/audio_processing_simulator.h @@ -106,7 +106,7 @@ struct SimulationSettings { absl::optional use_ts; absl::optional use_analog_agc; absl::optional use_all; - absl::optional analog_agc_disable_digital_adaptive; + absl::optional analog_agc_use_digital_adaptive_controller; absl::optional agc_mode; absl::optional agc_target_level; absl::optional use_agc_limiter; @@ -119,6 +119,7 @@ struct SimulationSettings { absl::optional analog_mic_gain_emulation_initial_level; absl::optional ns_level; absl::optional ns_analysis_on_linear_aec_output; + absl::optional override_key_pressed; absl::optional maximum_internal_processing_rate; int initial_mic_level; bool simulate_mic_gain = false; diff --git a/modules/audio_processing/test/audioproc_float_impl.cc b/modules/audio_processing/test/audioproc_float_impl.cc index aab1881913..dc38172666 100644 --- a/modules/audio_processing/test/audioproc_float_impl.cc +++ b/modules/audio_processing/test/audioproc_float_impl.cc @@ -111,8 +111,7 @@ ABSL_FLAG(int, ABSL_FLAG(int, ts, kParameterNotSpecifiedValue, - "Activate (1), deactivate (0) or activate the transient suppressor " - "with continuous key events (2)"); + "Activate (1) or deactivate (0) the transient suppressor"); ABSL_FLAG(int, analog_agc, kParameterNotSpecifiedValue, @@ -123,10 +122,10 @@ ABSL_FLAG(bool, "Activate all of the default components (will be overridden by any " "other settings)"); ABSL_FLAG(int, - analog_agc_disable_digital_adaptive, + analog_agc_use_digital_adaptive_controller, kParameterNotSpecifiedValue, - "Force-deactivate (1) digital adaptation in " - "experimental AGC. Digital adaptation is active by default (0)."); + "Activate (1) or deactivate (0) digital adaptation in AGC1. " + "Digital adaptation is active by default."); ABSL_FLAG(int, agc_mode, kParameterNotSpecifiedValue, @@ -218,6 +217,12 @@ ABSL_FLAG(int, simulated_mic_kind, kParameterNotSpecifiedValue, "Specify which microphone kind to use for microphone simulation"); +ABSL_FLAG(int, + override_key_pressed, + kParameterNotSpecifiedValue, + "Always set to true (1) or to false (0) the key press state. If " + "unspecified, false is set with Wav files or, with AEC dumps, the " + "recorded event is used."); ABSL_FLAG(int, frame_for_sending_capture_output_used_false, kParameterNotSpecifiedValue, @@ -412,8 +417,9 @@ SimulationSettings CreateSettings() { SetSettingIfSpecified(absl::GetFlag(FLAGS_ts), &settings.use_ts); SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc), &settings.use_analog_agc); - SetSettingIfFlagSet(absl::GetFlag(FLAGS_analog_agc_disable_digital_adaptive), - &settings.analog_agc_disable_digital_adaptive); + SetSettingIfFlagSet( + absl::GetFlag(FLAGS_analog_agc_use_digital_adaptive_controller), + &settings.analog_agc_use_digital_adaptive_controller); SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_mode), &settings.agc_mode); SetSettingIfSpecified(absl::GetFlag(FLAGS_agc_target_level), &settings.agc_target_level); @@ -458,6 +464,8 @@ SimulationSettings CreateSettings() { settings.simulate_mic_gain = absl::GetFlag(FLAGS_simulate_mic_gain); SetSettingIfSpecified(absl::GetFlag(FLAGS_simulated_mic_kind), &settings.simulated_mic_kind); + SetSettingIfFlagSet(absl::GetFlag(FLAGS_override_key_pressed), + &settings.override_key_pressed); SetSettingIfSpecified( absl::GetFlag(FLAGS_frame_for_sending_capture_output_used_false), &settings.frame_for_sending_capture_output_used_false); diff --git a/modules/audio_processing/test/conversational_speech/BUILD.gn b/modules/audio_processing/test/conversational_speech/BUILD.gn index 42707afda7..035f23144d 100644 --- a/modules/audio_processing/test/conversational_speech/BUILD.gn +++ b/modules/audio_processing/test/conversational_speech/BUILD.gn @@ -47,7 +47,9 @@ rtc_library("lib") { "../../../../api:array_view", "../../../../common_audio", "../../../../rtc_base:checks", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", + "../../../../rtc_base:safe_conversions", + "../../../../rtc_base:stringutils", "../../../../test:fileutils", ] visibility = [ ":*" ] # Only targets in this file can depend on this. @@ -66,7 +68,7 @@ rtc_library("unittest") { ":lib", "../../../../api:array_view", "../../../../common_audio", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", "../../../../test:fileutils", "../../../../test:test_support", "//testing/gtest", diff --git a/modules/audio_processing/test/py_quality_assessment/BUILD.gn b/modules/audio_processing/test/py_quality_assessment/BUILD.gn index 9ec86d17ec..581fe95bb3 100644 --- a/modules/audio_processing/test/py_quality_assessment/BUILD.gn +++ b/modules/audio_processing/test/py_quality_assessment/BUILD.gn @@ -105,7 +105,6 @@ if (!build_with_chromium) { output_dir = "${root_out_dir}/py_quality_assessment/quality_assessment" deps = [ "../../../../rtc_base:checks", - "../../../../rtc_base:rtc_base_approved", ] } @@ -114,7 +113,7 @@ if (!build_with_chromium) { sources = [ "quality_assessment/vad.cc" ] deps = [ "../../../../common_audio", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] @@ -126,7 +125,7 @@ if (!build_with_chromium) { deps = [ "../..", "../../../../common_audio", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", "../../vad", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", @@ -139,7 +138,7 @@ if (!build_with_chromium) { deps = [ "../..", "../../../../common_audio", - "../../../../rtc_base:rtc_base_approved", + "../../../../rtc_base:logging", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] diff --git a/modules/audio_processing/test/wav_based_simulator.cc b/modules/audio_processing/test/wav_based_simulator.cc index 10c0d3717e..d8f6b594ac 100644 --- a/modules/audio_processing/test/wav_based_simulator.cc +++ b/modules/audio_processing/test/wav_based_simulator.cc @@ -25,7 +25,7 @@ namespace test { std::vector WavBasedSimulator::GetCustomEventChain(const std::string& filename) { std::vector call_chain; - FileWrapper file_wrapper = FileWrapper::OpenReadOnly(filename.c_str()); + FileWrapper file_wrapper = FileWrapper::OpenReadOnly(filename); RTC_CHECK(file_wrapper.is_open()) << "Could not open the custom call order file, reverting " @@ -82,7 +82,7 @@ void WavBasedSimulator::PrepareProcessStreamCall() { if (settings_.fixed_interface) { fwd_frame_.CopyFrom(*in_buf_); } - ap_->set_stream_key_pressed(settings_.use_ts && (*settings_.use_ts)); + ap_->set_stream_key_pressed(settings_.override_key_pressed.value_or(false)); if (!settings_.use_stream_delay || *settings_.use_stream_delay) { RTC_CHECK_EQ(AudioProcessing::kNoError, diff --git a/modules/audio_processing/transient/BUILD.gn b/modules/audio_processing/transient/BUILD.gn index 5f9a13969a..9be0da2113 100644 --- a/modules/audio_processing/transient/BUILD.gn +++ b/modules/audio_processing/transient/BUILD.gn @@ -37,6 +37,7 @@ rtc_library("transient_suppressor_impl") { ] deps = [ ":transient_suppressor_api", + ":voice_probability_delay_unit", "../../../common_audio:common_audio", "../../../common_audio:common_audio_c", "../../../common_audio:fir_filter", @@ -48,6 +49,14 @@ rtc_library("transient_suppressor_impl") { ] } +rtc_library("voice_probability_delay_unit") { + sources = [ + "voice_probability_delay_unit.cc", + "voice_probability_delay_unit.h", + ] + deps = [ "../../../rtc_base:checks" ] +} + if (rtc_include_tests) { if (!build_with_chromium) { rtc_executable("click_annotate") { @@ -71,12 +80,14 @@ if (rtc_include_tests) { "file_utils.cc", "file_utils.h", "transient_suppression_test.cc", + "voice_probability_delay_unit_unittest.cc", ] deps = [ + ":transient_suppressor_api", ":transient_suppressor_impl", + ":voice_probability_delay_unit", "..:audio_processing", "../../../common_audio", - "../../../rtc_base:rtc_base_approved", "../../../rtc_base/system:file_wrapper", "../../../system_wrappers", "../../../test:fileutils", @@ -85,6 +96,7 @@ if (rtc_include_tests) { "//testing/gtest", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", + "//third_party/abseil-cpp/absl/types:optional", ] } } @@ -99,16 +111,20 @@ if (rtc_include_tests) { "moving_moments_unittest.cc", "transient_detector_unittest.cc", "transient_suppressor_unittest.cc", + "voice_probability_delay_unit_unittest.cc", "wpd_node_unittest.cc", "wpd_tree_unittest.cc", ] deps = [ + ":transient_suppressor_api", ":transient_suppressor_impl", + ":voice_probability_delay_unit", "../../../rtc_base:stringutils", "../../../rtc_base/system:file_wrapper", "../../../test:fileutils", "../../../test:test_support", "//testing/gtest", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } } diff --git a/modules/audio_processing/transient/file_utils_unittest.cc b/modules/audio_processing/transient/file_utils_unittest.cc index 1bcf6f95be..c32df6c81f 100644 --- a/modules/audio_processing/transient/file_utils_unittest.cc +++ b/modules/audio_processing/transient/file_utils_unittest.cc @@ -159,7 +159,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ConvertDoubleToByteArray) { TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16BufferFromFile) { std::string test_filename = kTestFileName; - FileWrapper file = FileWrapper::OpenReadOnly(test_filename.c_str()); + FileWrapper file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileName.c_str(); @@ -197,7 +197,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16BufferFromFile) { TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16FromFileToFloatBuffer) { std::string test_filename = kTestFileName; - FileWrapper file = FileWrapper::OpenReadOnly(test_filename.c_str()); + FileWrapper file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileName.c_str(); @@ -237,7 +237,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16FromFileToFloatBuffer) { TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16FromFileToDoubleBuffer) { std::string test_filename = kTestFileName; - FileWrapper file = FileWrapper::OpenReadOnly(test_filename.c_str()); + FileWrapper file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileName.c_str(); @@ -275,7 +275,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ReadInt16FromFileToDoubleBuffer) { TEST_F(TransientFileUtilsTest, MAYBE_ReadFloatBufferFromFile) { std::string test_filename = kTestFileNamef; - FileWrapper file = FileWrapper::OpenReadOnly(test_filename.c_str()); + FileWrapper file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileNamef.c_str(); @@ -311,7 +311,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ReadFloatBufferFromFile) { TEST_F(TransientFileUtilsTest, MAYBE_ReadDoubleBufferFromFile) { std::string test_filename = kTestFileName; - FileWrapper file = FileWrapper::OpenReadOnly(test_filename.c_str()); + FileWrapper file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileName.c_str(); @@ -348,7 +348,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteInt16BufferToFile) { std::string kOutFileName = CreateTempFilename(test::OutputPath(), "utils_test"); - FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName.c_str()); + FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -365,7 +365,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteInt16BufferToFile) { file.Close(); - file = FileWrapper::OpenReadOnly(kOutFileName.c_str()); + file = FileWrapper::OpenReadOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -384,7 +384,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteFloatBufferToFile) { std::string kOutFileName = CreateTempFilename(test::OutputPath(), "utils_test"); - FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName.c_str()); + FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -401,7 +401,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteFloatBufferToFile) { file.Close(); - file = FileWrapper::OpenReadOnly(kOutFileName.c_str()); + file = FileWrapper::OpenReadOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -420,7 +420,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteDoubleBufferToFile) { std::string kOutFileName = CreateTempFilename(test::OutputPath(), "utils_test"); - FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName.c_str()); + FileWrapper file = FileWrapper::OpenWriteOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -437,7 +437,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_WriteDoubleBufferToFile) { file.Close(); - file = FileWrapper::OpenReadOnly(kOutFileName.c_str()); + file = FileWrapper::OpenReadOnly(kOutFileName); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kOutFileName.c_str(); @@ -472,7 +472,7 @@ TEST_F(TransientFileUtilsTest, MAYBE_ExpectedErrorReturnValues) { EXPECT_EQ(0u, WriteInt16BufferToFile(&file, 1, int16_buffer.get())); EXPECT_EQ(0u, WriteDoubleBufferToFile(&file, 1, double_buffer.get())); - file = FileWrapper::OpenReadOnly(test_filename.c_str()); + file = FileWrapper::OpenReadOnly(test_filename); ASSERT_TRUE(file.is_open()) << "File could not be opened:\n" << kTestFileName.c_str(); diff --git a/modules/audio_processing/transient/transient_detector_unittest.cc b/modules/audio_processing/transient/transient_detector_unittest.cc index 0425133fba..a7364626fd 100644 --- a/modules/audio_processing/transient/transient_detector_unittest.cc +++ b/modules/audio_processing/transient/transient_detector_unittest.cc @@ -48,7 +48,7 @@ TEST(TransientDetectorTest, CorrectnessBasedOnFiles) { << (sample_rate_hz / 1000) << "kHz"; FileWrapper detect_file = FileWrapper::OpenReadOnly( - test::ResourcePath(detect_file_name.str(), "dat").c_str()); + test::ResourcePath(detect_file_name.str(), "dat")); bool file_opened = detect_file.is_open(); ASSERT_TRUE(file_opened) << "File could not be opened.\n" @@ -60,7 +60,7 @@ TEST(TransientDetectorTest, CorrectnessBasedOnFiles) { << (sample_rate_hz / 1000) << "kHz"; FileWrapper audio_file = FileWrapper::OpenReadOnly( - test::ResourcePath(audio_file_name.str(), "pcm").c_str()); + test::ResourcePath(audio_file_name.str(), "pcm")); // Create detector. TransientDetector detector(sample_rate_hz); diff --git a/modules/audio_processing/transient/transient_suppression_test.cc b/modules/audio_processing/transient/transient_suppression_test.cc index 21409132d2..2d8baf9416 100644 --- a/modules/audio_processing/transient/transient_suppression_test.cc +++ b/modules/audio_processing/transient/transient_suppression_test.cc @@ -20,6 +20,7 @@ #include "absl/flags/parse.h" #include "common_audio/include/audio_util.h" #include "modules/audio_processing/agc/agc.h" +#include "modules/audio_processing/transient/transient_suppressor.h" #include "modules/audio_processing/transient/transient_suppressor_impl.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -165,9 +166,10 @@ void void_main() { Agc agc; - TransientSuppressorImpl suppressor; - suppressor.Initialize(absl::GetFlag(FLAGS_sample_rate_hz), detection_rate_hz, - absl::GetFlag(FLAGS_num_channels)); + TransientSuppressorImpl suppressor(TransientSuppressor::VadMode::kDefault, + absl::GetFlag(FLAGS_sample_rate_hz), + detection_rate_hz, + absl::GetFlag(FLAGS_num_channels)); const size_t audio_buffer_size = absl::GetFlag(FLAGS_chunk_size_ms) * absl::GetFlag(FLAGS_sample_rate_hz) / 1000; @@ -198,12 +200,11 @@ void void_main() { audio_buffer_f[i] = audio_buffer_i[i]; } - ASSERT_EQ(0, suppressor.Suppress( - audio_buffer_f.get(), audio_buffer_size, - absl::GetFlag(FLAGS_num_channels), detection_buffer.get(), - detection_buffer_size, reference_buffer.get(), - audio_buffer_size, agc.voice_probability(), true)) - << "The transient suppressor could not suppress the frame"; + suppressor.Suppress(audio_buffer_f.get(), audio_buffer_size, + absl::GetFlag(FLAGS_num_channels), + detection_buffer.get(), detection_buffer_size, + reference_buffer.get(), audio_buffer_size, + agc.voice_probability(), true); // Write result to out file. WritePCM(out_file, audio_buffer_size, absl::GetFlag(FLAGS_num_channels), diff --git a/modules/audio_processing/transient/transient_suppressor.h b/modules/audio_processing/transient/transient_suppressor.h index 982ddbd0ec..ecb3c3baab 100644 --- a/modules/audio_processing/transient/transient_suppressor.h +++ b/modules/audio_processing/transient/transient_suppressor.h @@ -11,9 +11,7 @@ #ifndef MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ #define MODULES_AUDIO_PROCESSING_TRANSIENT_TRANSIENT_SUPPRESSOR_H_ -#include -#include -#include +#include namespace webrtc { @@ -21,11 +19,26 @@ namespace webrtc { // restoration algorithm that attenuates unexpected spikes in the spectrum. class TransientSuppressor { public: + // Type of VAD used by the caller to compute the `voice_probability` argument + // `Suppress()`. + enum class VadMode { + // By default, `TransientSuppressor` assumes that `voice_probability` is + // computed by `AgcManagerDirect`. + kDefault = 0, + // Use this mode when `TransientSuppressor` must assume that + // `voice_probability` is computed by the RNN VAD. + kRnnVad, + // Use this mode to let `TransientSuppressor::Suppressor()` ignore + // `voice_probability` and behave as if voice information is unavailable + // (regardless of the passed value). + kNoVad, + }; + virtual ~TransientSuppressor() {} - virtual int Initialize(int sample_rate_hz, - int detector_rate_hz, - int num_channels) = 0; + virtual void Initialize(int sample_rate_hz, + int detector_rate_hz, + int num_channels) = 0; // Processes a `data` chunk, and returns it with keystrokes suppressed from // it. The float format is assumed to be int16 ranged. If there are more than @@ -43,16 +56,18 @@ class TransientSuppressor { // of audio. If voice information is not available, `voice_probability` must // always be set to 1. // `key_pressed` determines if a key was pressed on this audio chunk. - // Returns 0 on success and -1 otherwise. - virtual int Suppress(float* data, - size_t data_length, - int num_channels, - const float* detection_data, - size_t detection_length, - const float* reference_data, - size_t reference_length, - float voice_probability, - bool key_pressed) = 0; + // Returns a delayed version of `voice_probability` according to the + // algorithmic delay introduced by this method. In this way, the modified + // `data` and the returned voice probability will be temporally aligned. + virtual float Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) = 0; }; } // namespace webrtc diff --git a/modules/audio_processing/transient/transient_suppressor_impl.cc b/modules/audio_processing/transient/transient_suppressor_impl.cc index f8161f6428..90428464e3 100644 --- a/modules/audio_processing/transient/transient_suppressor_impl.cc +++ b/modules/audio_processing/transient/transient_suppressor_impl.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "common_audio/include/audio_util.h" #include "common_audio/signal_processing/include/signal_processing_library.h" @@ -32,7 +33,6 @@ namespace webrtc { static const float kMeanIIRCoefficient = 0.5f; -static const float kVoiceThreshold = 0.02f; // TODO(aluebs): Check if these values work also for 48kHz. static const size_t kMinVoiceBin = 3; @@ -44,10 +44,27 @@ float ComplexMagnitude(float a, float b) { return std::abs(a) + std::abs(b); } +std::string GetVadModeLabel(TransientSuppressor::VadMode vad_mode) { + switch (vad_mode) { + case TransientSuppressor::VadMode::kDefault: + return "default"; + case TransientSuppressor::VadMode::kRnnVad: + return "RNN VAD"; + case TransientSuppressor::VadMode::kNoVad: + return "no VAD"; + } +} + } // namespace -TransientSuppressorImpl::TransientSuppressorImpl() - : data_length_(0), +TransientSuppressorImpl::TransientSuppressorImpl(VadMode vad_mode, + int sample_rate_hz, + int detector_rate_hz, + int num_channels) + : vad_mode_(vad_mode), + voice_probability_delay_unit_(/*delay_num_samples=*/0, sample_rate_hz), + analyzed_audio_is_silent_(false), + data_length_(0), detection_length_(0), analysis_length_(0), buffer_delay_(0), @@ -62,13 +79,26 @@ TransientSuppressorImpl::TransientSuppressorImpl() use_hard_restoration_(false), chunks_since_voice_change_(0), seed_(182), - using_reference_(false) {} + using_reference_(false) { + RTC_LOG(LS_INFO) << "VAD mode: " << GetVadModeLabel(vad_mode_); + Initialize(sample_rate_hz, detector_rate_hz, num_channels); +} TransientSuppressorImpl::~TransientSuppressorImpl() {} -int TransientSuppressorImpl::Initialize(int sample_rate_hz, - int detection_rate_hz, - int num_channels) { +void TransientSuppressorImpl::Initialize(int sample_rate_hz, + int detection_rate_hz, + int num_channels) { + RTC_DCHECK(sample_rate_hz == ts::kSampleRate8kHz || + sample_rate_hz == ts::kSampleRate16kHz || + sample_rate_hz == ts::kSampleRate32kHz || + sample_rate_hz == ts::kSampleRate48kHz); + RTC_DCHECK(detection_rate_hz == ts::kSampleRate8kHz || + detection_rate_hz == ts::kSampleRate16kHz || + detection_rate_hz == ts::kSampleRate32kHz || + detection_rate_hz == ts::kSampleRate48kHz); + RTC_DCHECK_GT(num_channels, 0); + switch (sample_rate_hz) { case ts::kSampleRate8kHz: analysis_length_ = 128u; @@ -87,26 +117,18 @@ int TransientSuppressorImpl::Initialize(int sample_rate_hz, window_ = kBlocks480w1024; break; default: - return -1; - } - if (detection_rate_hz != ts::kSampleRate8kHz && - detection_rate_hz != ts::kSampleRate16kHz && - detection_rate_hz != ts::kSampleRate32kHz && - detection_rate_hz != ts::kSampleRate48kHz) { - return -1; - } - if (num_channels <= 0) { - return -1; + RTC_DCHECK_NOTREACHED(); + return; } detector_.reset(new TransientDetector(detection_rate_hz)); data_length_ = sample_rate_hz * ts::kChunkSizeMs / 1000; - if (data_length_ > analysis_length_) { - RTC_DCHECK_NOTREACHED(); - return -1; - } + RTC_DCHECK_LE(data_length_, analysis_length_); buffer_delay_ = analysis_length_ - data_length_; + voice_probability_delay_unit_.Initialize(/*delay_num_samples=*/buffer_delay_, + sample_rate_hz); + complex_analysis_length_ = analysis_length_ / 2 + 1; RTC_DCHECK_GE(complex_analysis_length_, kMaxVoiceBin); num_channels_ = num_channels; @@ -155,28 +177,28 @@ int TransientSuppressorImpl::Initialize(int sample_rate_hz, chunks_since_voice_change_ = 0; seed_ = 182; using_reference_ = false; - return 0; } -int TransientSuppressorImpl::Suppress(float* data, - size_t data_length, - int num_channels, - const float* detection_data, - size_t detection_length, - const float* reference_data, - size_t reference_length, - float voice_probability, - bool key_pressed) { +float TransientSuppressorImpl::Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) { if (!data || data_length != data_length_ || num_channels != num_channels_ || detection_length != detection_length_ || voice_probability < 0 || voice_probability > 1) { - return -1; + // The audio is not modified, so the voice probability is returned as is + // (delay not applied). + return voice_probability; } UpdateKeypress(key_pressed); UpdateBuffers(data); - int result = 0; if (detection_enabled_) { UpdateRestoration(voice_probability); @@ -189,7 +211,9 @@ int TransientSuppressorImpl::Suppress(float* data, float detector_result = detector_->Detect(detection_data, detection_length, reference_data, reference_length); if (detector_result < 0) { - return -1; + // The audio is not modified, so the voice probability is returned as is + // (delay not applied). + return voice_probability; } using_reference_ = detector_->using_reference(); @@ -219,7 +243,9 @@ int TransientSuppressorImpl::Suppress(float* data, : &in_buffer_[i * analysis_length_], data_length_ * sizeof(*data)); } - return result; + + // The audio has been modified, return the delayed voice probability. + return voice_probability_delay_unit_.Delay(voice_probability); } // This should only be called when detection is enabled. UpdateBuffers() must @@ -304,16 +330,34 @@ void TransientSuppressorImpl::UpdateKeypress(bool key_pressed) { } void TransientSuppressorImpl::UpdateRestoration(float voice_probability) { - const int kHardRestorationOffsetDelay = 3; - const int kHardRestorationOnsetDelay = 80; - - bool not_voiced = voice_probability < kVoiceThreshold; + bool not_voiced; + switch (vad_mode_) { + case TransientSuppressor::VadMode::kDefault: { + constexpr float kVoiceThreshold = 0.02f; + not_voiced = voice_probability < kVoiceThreshold; + break; + } + case TransientSuppressor::VadMode::kRnnVad: { + constexpr float kVoiceThreshold = 0.7f; + not_voiced = voice_probability < kVoiceThreshold; + break; + } + case TransientSuppressor::VadMode::kNoVad: + // Always assume that voice is detected. + not_voiced = false; + break; + } if (not_voiced == use_hard_restoration_) { chunks_since_voice_change_ = 0; } else { ++chunks_since_voice_change_; + // Number of 10 ms frames to wait to transition to and from hard + // restoration. + constexpr int kHardRestorationOffsetDelay = 3; + constexpr int kHardRestorationOnsetDelay = 80; + if ((use_hard_restoration_ && chunks_since_voice_change_ > kHardRestorationOffsetDelay) || (!use_hard_restoration_ && diff --git a/modules/audio_processing/transient/transient_suppressor_impl.h b/modules/audio_processing/transient/transient_suppressor_impl.h index fa8186eed9..4005a16b0a 100644 --- a/modules/audio_processing/transient/transient_suppressor_impl.h +++ b/modules/audio_processing/transient/transient_suppressor_impl.h @@ -17,6 +17,7 @@ #include #include "modules/audio_processing/transient/transient_suppressor.h" +#include "modules/audio_processing/transient/voice_probability_delay_unit.h" #include "rtc_base/gtest_prod_util.h" namespace webrtc { @@ -27,42 +28,28 @@ class TransientDetector; // restoration algorithm that attenuates unexpected spikes in the spectrum. class TransientSuppressorImpl : public TransientSuppressor { public: - TransientSuppressorImpl(); + TransientSuppressorImpl(VadMode vad_mode, + int sample_rate_hz, + int detector_rate_hz, + int num_channels); ~TransientSuppressorImpl() override; - int Initialize(int sample_rate_hz, - int detector_rate_hz, - int num_channels) override; - - // Processes a `data` chunk, and returns it with keystrokes suppressed from - // it. The float format is assumed to be int16 ranged. If there are more than - // one channel, the chunks are concatenated one after the other in `data`. - // `data_length` must be equal to `data_length_`. - // `num_channels` must be equal to `num_channels_`. - // A sub-band, ideally the higher, can be used as `detection_data`. If it is - // NULL, `data` is used for the detection too. The `detection_data` is always - // assumed mono. - // If a reference signal (e.g. keyboard microphone) is available, it can be - // passed in as `reference_data`. It is assumed mono and must have the same - // length as `data`. NULL is accepted if unavailable. - // This suppressor performs better if voice information is available. - // `voice_probability` is the probability of voice being present in this chunk - // of audio. If voice information is not available, `voice_probability` must - // always be set to 1. - // `key_pressed` determines if a key was pressed on this audio chunk. - // Returns 0 on success and -1 otherwise. - int Suppress(float* data, - size_t data_length, - int num_channels, - const float* detection_data, - size_t detection_length, - const float* reference_data, - size_t reference_length, - float voice_probability, - bool key_pressed) override; + void Initialize(int sample_rate_hz, + int detector_rate_hz, + int num_channels) override; + + float Suppress(float* data, + size_t data_length, + int num_channels, + const float* detection_data, + size_t detection_length, + const float* reference_data, + size_t reference_length, + float voice_probability, + bool key_pressed) override; private: - FRIEND_TEST_ALL_PREFIXES(TransientSuppressorImplTest, + FRIEND_TEST_ALL_PREFIXES(TransientSuppressorVadModeParametrization, TypingDetectionLogicWorksAsExpectedForMono); void Suppress(float* in_ptr, float* spectral_mean, float* out_ptr); @@ -74,8 +61,13 @@ class TransientSuppressorImpl : public TransientSuppressor { void HardRestoration(float* spectral_mean); void SoftRestoration(float* spectral_mean); + const VadMode vad_mode_; + VoiceProbabilityDelayUnit voice_probability_delay_unit_; + std::unique_ptr detector_; + bool analyzed_audio_is_silent_; + size_t data_length_; size_t detection_length_; size_t analysis_length_; diff --git a/modules/audio_processing/transient/transient_suppressor_unittest.cc b/modules/audio_processing/transient/transient_suppressor_unittest.cc index a5c6bb1922..ab48504af6 100644 --- a/modules/audio_processing/transient/transient_suppressor_unittest.cc +++ b/modules/audio_processing/transient/transient_suppressor_unittest.cc @@ -8,18 +8,39 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/audio_processing/transient/transient_suppressor_impl.h" +#include "modules/audio_processing/transient/transient_suppressor.h" + +#include +#include "absl/types/optional.h" #include "modules/audio_processing/transient/common.h" +#include "modules/audio_processing/transient/transient_suppressor_impl.h" #include "test/gtest.h" namespace webrtc { +namespace { +constexpr int kMono = 1; + +// Returns the index of the first non-zero sample in `samples` or an unspecified +// value if no value is zero. +absl::optional FindFirstNonZeroSample(const std::vector& samples) { + for (size_t i = 0; i < samples.size(); ++i) { + if (samples[i] != 0.0f) { + return i; + } + } + return absl::nullopt; +} + +} // namespace -TEST(TransientSuppressorImplTest, TypingDetectionLogicWorksAsExpectedForMono) { - static const int kNumChannels = 1; +class TransientSuppressorVadModeParametrization + : public ::testing::TestWithParam {}; - TransientSuppressorImpl ts; - ts.Initialize(ts::kSampleRate16kHz, ts::kSampleRate16kHz, kNumChannels); +TEST_P(TransientSuppressorVadModeParametrization, + TypingDetectionLogicWorksAsExpectedForMono) { + TransientSuppressorImpl ts(GetParam(), ts::kSampleRate16kHz, + ts::kSampleRate16kHz, kMono); // Each key-press enables detection. EXPECT_FALSE(ts.detection_enabled_); @@ -82,4 +103,73 @@ TEST(TransientSuppressorImplTest, TypingDetectionLogicWorksAsExpectedForMono) { } } +INSTANTIATE_TEST_SUITE_P( + TransientSuppressorImplTest, + TransientSuppressorVadModeParametrization, + ::testing::Values(TransientSuppressor::VadMode::kDefault, + TransientSuppressor::VadMode::kRnnVad, + TransientSuppressor::VadMode::kNoVad)); + +class TransientSuppressorSampleRateParametrization + : public ::testing::TestWithParam {}; + +// Checks that voice probability and processed audio data are temporally aligned +// after `Suppress()` is called. +TEST_P(TransientSuppressorSampleRateParametrization, + CheckAudioAndVoiceProbabilityTemporallyAligned) { + const int sample_rate_hz = GetParam(); + TransientSuppressorImpl ts(TransientSuppressor::VadMode::kDefault, + sample_rate_hz, + /*detection_rate_hz=*/sample_rate_hz, kMono); + + const int frame_size = sample_rate_hz * ts::kChunkSizeMs / 1000; + std::vector frame(frame_size); + + constexpr int kMaxAttempts = 3; + for (int i = 0; i < kMaxAttempts; ++i) { + SCOPED_TRACE(i); + + // Call `Suppress()` on frames of non-zero audio samples. + std::fill(frame.begin(), frame.end(), 1000.0f); + float delayed_voice_probability = ts.Suppress( + frame.data(), frame.size(), kMono, /*detection_data=*/nullptr, + /*detection_length=*/frame_size, /*reference_data=*/nullptr, + /*reference_length=*/frame_size, /*voice_probability=*/1.0f, + /*key_pressed=*/false); + + // Detect the algorithmic delay of `TransientSuppressorImpl`. + absl::optional frame_delay = FindFirstNonZeroSample(frame); + + // Check that the delayed voice probability is delayed according to the + // measured delay. + if (frame_delay.has_value()) { + if (*frame_delay == 0) { + // When the delay is a multiple integer of the frame duration, + // `Suppress()` returns a copy of a previously observed voice + // probability value. + EXPECT_EQ(delayed_voice_probability, 1.0f); + } else { + // Instead, when the delay is fractional, `Suppress()` returns an + // interpolated value. Since the exact value depends on the + // interpolation method, we only check that the delayed voice + // probability is not zero as it must converge towards the previoulsy + // observed value. + EXPECT_GT(delayed_voice_probability, 0.0f); + } + break; + } else { + // The algorithmic delay is longer than the duration of a single frame. + // Until the delay is detected, the delayed voice probability is zero. + EXPECT_EQ(delayed_voice_probability, 0.0f); + } + } +} + +INSTANTIATE_TEST_SUITE_P(TransientSuppressorImplTest, + TransientSuppressorSampleRateParametrization, + ::testing::Values(ts::kSampleRate8kHz, + ts::kSampleRate16kHz, + ts::kSampleRate32kHz, + ts::kSampleRate48kHz)); + } // namespace webrtc diff --git a/modules/audio_processing/transient/voice_probability_delay_unit.cc b/modules/audio_processing/transient/voice_probability_delay_unit.cc new file mode 100644 index 0000000000..27b2b42b38 --- /dev/null +++ b/modules/audio_processing/transient/voice_probability_delay_unit.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 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/transient/voice_probability_delay_unit.h" + +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +VoiceProbabilityDelayUnit::VoiceProbabilityDelayUnit(int delay_num_samples, + int sample_rate_hz) { + Initialize(delay_num_samples, sample_rate_hz); +} + +void VoiceProbabilityDelayUnit::Initialize(int delay_num_samples, + int sample_rate_hz) { + RTC_DCHECK_GE(delay_num_samples, 0); + RTC_DCHECK_LE(delay_num_samples, sample_rate_hz / 50) + << "The implementation does not support delays greater than 20 ms."; + int frame_size = rtc::CheckedDivExact(sample_rate_hz, 100); // 10 ms. + if (delay_num_samples <= frame_size) { + weights_[0] = 0.0f; + weights_[1] = static_cast(delay_num_samples) / frame_size; + weights_[2] = + static_cast(frame_size - delay_num_samples) / frame_size; + } else { + delay_num_samples -= frame_size; + weights_[0] = static_cast(delay_num_samples) / frame_size; + weights_[1] = + static_cast(frame_size - delay_num_samples) / frame_size; + weights_[2] = 0.0f; + } + + // Resets the delay unit. + last_probabilities_.fill(0.0f); +} + +float VoiceProbabilityDelayUnit::Delay(float voice_probability) { + float weighted_probability = weights_[0] * last_probabilities_[0] + + weights_[1] * last_probabilities_[1] + + weights_[2] * voice_probability; + last_probabilities_[0] = last_probabilities_[1]; + last_probabilities_[1] = voice_probability; + return weighted_probability; +} + +} // namespace webrtc diff --git a/modules/audio_processing/transient/voice_probability_delay_unit.h b/modules/audio_processing/transient/voice_probability_delay_unit.h new file mode 100644 index 0000000000..05961663e3 --- /dev/null +++ b/modules/audio_processing/transient/voice_probability_delay_unit.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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_TRANSIENT_VOICE_PROBABILITY_DELAY_UNIT_H_ +#define MODULES_AUDIO_PROCESSING_TRANSIENT_VOICE_PROBABILITY_DELAY_UNIT_H_ + +#include + +namespace webrtc { + +// Iteratively produces a sequence of delayed voice probability values given a +// fixed delay between 0 and 20 ms and given a sequence of voice probability +// values observed every 10 ms. Supports fractional delays, that are delays +// which are not a multiple integer of 10 ms. Applies interpolation with +// fractional delays; otherwise, returns a previously observed value according +// to the given fixed delay. +class VoiceProbabilityDelayUnit { + public: + // Ctor. `delay_num_samples` is the delay in number of samples and it must be + // non-negative and less than 20 ms. + VoiceProbabilityDelayUnit(int delay_num_samples, int sample_rate_hz); + + // Handles delay and sample rate changes and resets the delay unit. + void Initialize(int delay_num_samples, int sample_rate_hz); + + // Observes `voice_probability` and returns a delayed voice probability. + float Delay(float voice_probability); + + private: + std::array weights_; + std::array last_probabilities_; +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_TRANSIENT_VOICE_PROBABILITY_DELAY_UNIT_H_ diff --git a/modules/audio_processing/transient/voice_probability_delay_unit_unittest.cc b/modules/audio_processing/transient/voice_probability_delay_unit_unittest.cc new file mode 100644 index 0000000000..04848e6f2c --- /dev/null +++ b/modules/audio_processing/transient/voice_probability_delay_unit_unittest.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022 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/transient/voice_probability_delay_unit.h" + +#include "test/gtest.h" + +namespace webrtc { +namespace { + +// Checks that with zero delay, the observed value is immediately returned as +// delayed value. +TEST(VoiceProbabilityDelayUnit, NoDelay) { + VoiceProbabilityDelayUnit delay_unit(/*delay_num_samples=*/0, + /*sample_rate_hz=*/48000); + constexpr int kMax = 5; + for (int i = 0; i <= kMax; ++i) { + SCOPED_TRACE(i); + float voice_probability = static_cast(i) / kMax; + EXPECT_EQ(voice_probability, delay_unit.Delay(voice_probability)); + } +} + +// Checks that with integer delays, an exact copy of a previously observed value +// is returned. +TEST(VoiceProbabilityDelayUnit, IntegerDelay) { + VoiceProbabilityDelayUnit delay_unit_10ms(/*delay_num_samples=*/480, + /*sample_rate_hz=*/48000); + delay_unit_10ms.Delay(0.125f); + EXPECT_EQ(0.125f, delay_unit_10ms.Delay(0.9f)); + + VoiceProbabilityDelayUnit delay_unit_20ms(/*delay_num_samples=*/960, + /*sample_rate_hz=*/48000); + delay_unit_20ms.Delay(0.125f); + delay_unit_20ms.Delay(0.8f); + EXPECT_EQ(0.125f, delay_unit_20ms.Delay(0.9f)); +} + +// Checks that with a fractional delay < 10 ms, interpolation is applied. +TEST(VoiceProbabilityDelayUnit, FractionalDelayLessThan10ms) { + // Create delay unit with fractional delay of 6 ms. + VoiceProbabilityDelayUnit delay_unit(/*delay_num_samples=*/288, + /*sample_rate_hz=*/48000); + // frame 0 + // --------- frame 1 + // --------- + // 0000001111 + delay_unit.Delay(1.0f); + EXPECT_FLOAT_EQ(0.68f, delay_unit.Delay(0.2f)); +} + +// Checks that with a fractional delay > 10 ms, interpolation is applied. +TEST(VoiceProbabilityDelayUnit, FractionalDelayGreaterThan10ms) { + // Create delay unit with fractional delay of 14 ms. + VoiceProbabilityDelayUnit delay_unit(/*delay_num_samples=*/672, + /*sample_rate_hz=*/48000); + // frame 0 + // --------- frame 1 + // --------- frame 2 + // --------- + // 0000111111 + delay_unit.Delay(1.0f); + delay_unit.Delay(0.2f); + EXPECT_FLOAT_EQ(0.52f, delay_unit.Delay(1.0f)); +} + +// Checks that `Initialize()` resets the delay unit. +TEST(VoiceProbabilityDelayUnit, InitializeResetsDelayUnit) { + VoiceProbabilityDelayUnit delay_unit(/*delay_num_samples=*/960, + /*sample_rate_hz=*/48000); + delay_unit.Delay(1.0f); + delay_unit.Delay(0.9f); + + delay_unit.Initialize(/*delay_num_samples=*/160, /*sample_rate_hz=*/8000); + EXPECT_EQ(0.0f, delay_unit.Delay(0.1f)); + EXPECT_EQ(0.0f, delay_unit.Delay(0.2f)); + EXPECT_EQ(0.1f, delay_unit.Delay(0.3f)); +} + +// Checks that `Initialize()` handles delay changes. +TEST(VoiceProbabilityDelayUnit, InitializeHandlesDelayChanges) { + // Start with a 20 ms delay. + VoiceProbabilityDelayUnit delay_unit(/*delay_num_samples=*/960, + /*sample_rate_hz=*/48000); + delay_unit.Delay(1.0f); + delay_unit.Delay(0.9f); + + // Lower the delay to 10 ms. + delay_unit.Initialize(/*delay_num_samples=*/80, /*sample_rate_hz=*/8000); + EXPECT_EQ(0.0f, delay_unit.Delay(0.1f)); + EXPECT_EQ(0.1f, delay_unit.Delay(0.2f)); + + // Increase the delay to 15 ms. + delay_unit.Initialize(/*delay_num_samples=*/120, /*sample_rate_hz=*/8000); + EXPECT_EQ(0.0f, delay_unit.Delay(0.1f)); + EXPECT_EQ(0.05f, delay_unit.Delay(0.2f)); + EXPECT_EQ(0.15f, delay_unit.Delay(0.3f)); +} + +} // namespace +} // namespace webrtc diff --git a/modules/audio_processing/transient/wpd_tree_unittest.cc b/modules/audio_processing/transient/wpd_tree_unittest.cc index 97d69aea3f..bf3ff987d7 100644 --- a/modules/audio_processing/transient/wpd_tree_unittest.cc +++ b/modules/audio_processing/transient/wpd_tree_unittest.cc @@ -88,7 +88,7 @@ TEST(WPDTreeTest, CorrectnessBasedOnMatlabFiles) { rtc::StringBuilder matlab_stream; matlab_stream << "audio_processing/transient/wpd" << i; std::string matlab_string = test::ResourcePath(matlab_stream.str(), "dat"); - matlab_files_data[i] = FileWrapper::OpenReadOnly(matlab_string.c_str()); + matlab_files_data[i] = FileWrapper::OpenReadOnly(matlab_string); bool file_opened = matlab_files_data[i].is_open(); ASSERT_TRUE(file_opened) << "File could not be opened.\n" << matlab_string; @@ -98,7 +98,7 @@ TEST(WPDTreeTest, CorrectnessBasedOnMatlabFiles) { out_stream << test::OutputPath() << "wpd_" << i << ".out"; std::string out_string = out_stream.str(); - out_files_data[i] = FileWrapper::OpenWriteOnly(out_string.c_str()); + out_files_data[i] = FileWrapper::OpenWriteOnly(out_string); file_opened = out_files_data[i].is_open(); ASSERT_TRUE(file_opened) << "File could not be opened.\n" << out_string; @@ -108,7 +108,7 @@ TEST(WPDTreeTest, CorrectnessBasedOnMatlabFiles) { std::string test_file_name = test::ResourcePath( "audio_processing/transient/ajm-macbook-1-spke16m", "pcm"); - FileWrapper test_file = FileWrapper::OpenReadOnly(test_file_name.c_str()); + FileWrapper test_file = FileWrapper::OpenReadOnly(test_file_name); bool file_opened = test_file.is_open(); ASSERT_TRUE(file_opened) << "File could not be opened.\n" << test_file_name; diff --git a/modules/audio_processing/typing_detection.cc b/modules/audio_processing/typing_detection.cc deleted file mode 100644 index e725b264ee..0000000000 --- a/modules/audio_processing/typing_detection.cc +++ /dev/null @@ -1,93 +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. - */ - -#include "modules/audio_processing/typing_detection.h" - -namespace webrtc { - -TypingDetection::TypingDetection() - : time_active_(0), - time_since_last_typing_(0), - penalty_counter_(0), - counter_since_last_detection_update_(0), - detection_to_report_(false), - new_detection_to_report_(false), - time_window_(10), - cost_per_typing_(100), - reporting_threshold_(300), - penalty_decay_(1), - type_event_delay_(2), - report_detection_update_period_(1) {} - -TypingDetection::~TypingDetection() {} - -bool TypingDetection::Process(bool key_pressed, bool vad_activity) { - if (vad_activity) - time_active_++; - else - time_active_ = 0; - - // Keep track if time since last typing event - if (key_pressed) - time_since_last_typing_ = 0; - else - ++time_since_last_typing_; - - if (time_since_last_typing_ < type_event_delay_ && vad_activity && - time_active_ < time_window_) { - penalty_counter_ += cost_per_typing_; - if (penalty_counter_ > reporting_threshold_) - new_detection_to_report_ = true; - } - - if (penalty_counter_ > 0) - penalty_counter_ -= penalty_decay_; - - if (++counter_since_last_detection_update_ == - report_detection_update_period_) { - detection_to_report_ = new_detection_to_report_; - new_detection_to_report_ = false; - counter_since_last_detection_update_ = 0; - } - - return detection_to_report_; -} - -int TypingDetection::TimeSinceLastDetectionInSeconds() { - // Round to whole seconds. - return (time_since_last_typing_ + 50) / 100; -} - -void TypingDetection::SetParameters(int time_window, - int cost_per_typing, - int reporting_threshold, - int penalty_decay, - int type_event_delay, - int report_detection_update_period) { - if (time_window) - time_window_ = time_window; - - if (cost_per_typing) - cost_per_typing_ = cost_per_typing; - - if (reporting_threshold) - reporting_threshold_ = reporting_threshold; - - if (penalty_decay) - penalty_decay_ = penalty_decay; - - if (type_event_delay) - type_event_delay_ = type_event_delay; - - if (report_detection_update_period) - report_detection_update_period_ = report_detection_update_period; -} - -} // namespace webrtc diff --git a/modules/audio_processing/typing_detection.h b/modules/audio_processing/typing_detection.h deleted file mode 100644 index 9d96583b98..0000000000 --- a/modules/audio_processing/typing_detection.h +++ /dev/null @@ -1,92 +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. - */ - -#ifndef MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ -#define MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ - -#include "rtc_base/system/rtc_export.h" - -namespace webrtc { - -class RTC_EXPORT TypingDetection { - public: - TypingDetection(); - virtual ~TypingDetection(); - - // Run the detection algortihm. Shall be called every 10 ms. Returns true if - // typing is detected, or false if not, based on the update period as set with - // SetParameters(). See `report_detection_update_period_` description below. - bool Process(bool key_pressed, bool vad_activity); - - // Gets the time in seconds since the last detection. - int TimeSinceLastDetectionInSeconds(); - - // Sets the algorithm parameters. A parameter value of 0 leaves it unchanged. - // See the correspondning member variables below for descriptions. - void SetParameters(int time_window, - int cost_per_typing, - int reporting_threshold, - int penalty_decay, - int type_event_delay, - int report_detection_update_period); - - private: - int time_active_; - int time_since_last_typing_; - int penalty_counter_; - - // Counter since last time the detection status reported by Process() was - // updated. See also `report_detection_update_period_`. - int counter_since_last_detection_update_; - - // The detection status to report. Updated every - // `report_detection_update_period_` call to Process(). - bool detection_to_report_; - - // What `detection_to_report_` should be set to next time it is updated. - bool new_detection_to_report_; - - // Settable threshold values. - - // Number of 10 ms slots accepted to count as a hit. - int time_window_; - - // Penalty added for a typing + activity coincide. - int cost_per_typing_; - - // Threshold for `penalty_counter_`. - int reporting_threshold_; - - // How much we reduce `penalty_counter_` every 10 ms. - int penalty_decay_; - - // How old typing events we allow. - int type_event_delay_; - - // Settable update period. - - // Number of 10 ms slots between each update of the detection status returned - // by Process(). This inertia added to the algorithm is usually desirable and - // provided so that consumers of the class don't have to implement that - // themselves if they don't wish. - // If set to 1, each call to Process() will return the detection status for - // that 10 ms slot. - // If set to N (where N > 1), the detection status returned from Process() - // will remain the same until Process() has been called N times. Then, if none - // of the last N calls to Process() has detected typing for each respective - // 10 ms slot, Process() will return false. If at least one of the last N - // calls has detected typing, Process() will return true. And that returned - // status will then remain the same until the next N calls have been done. - int report_detection_update_period_; -}; - -} // namespace webrtc - -#endif // #ifndef MODULES_AUDIO_PROCESSING_TYPING_DETECTION_H_ diff --git a/modules/audio_processing/utility/BUILD.gn b/modules/audio_processing/utility/BUILD.gn index 437b544fc9..4851e77b03 100644 --- a/modules/audio_processing/utility/BUILD.gn +++ b/modules/audio_processing/utility/BUILD.gn @@ -50,7 +50,6 @@ if (rtc_include_tests) { sources = [ "cascaded_biquad_filter_unittest.cc" ] deps = [ ":cascaded_biquad_filter", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "//testing/gtest", ] @@ -62,7 +61,6 @@ if (rtc_include_tests) { sources = [ "delay_estimator_unittest.cc" ] deps = [ ":legacy_delay_estimator", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "//testing/gtest", ] diff --git a/modules/audio_processing/utility/delay_estimator_wrapper.cc b/modules/audio_processing/utility/delay_estimator_wrapper.cc index 521a8a0997..3b1409cc0b 100644 --- a/modules/audio_processing/utility/delay_estimator_wrapper.cc +++ b/modules/audio_processing/utility/delay_estimator_wrapper.cc @@ -21,8 +21,8 @@ namespace webrtc { // Only bit `kBandFirst` through bit `kBandLast` are processed and // `kBandFirst` - `kBandLast` must be < 32. -enum { kBandFirst = 12 }; -enum { kBandLast = 43 }; +constexpr int kBandFirst = 12; +constexpr int kBandLast = 43; static __inline uint32_t SetBit(uint32_t in, int pos) { uint32_t mask = (1 << pos); diff --git a/modules/audio_processing/vad/vad_audio_proc.h b/modules/audio_processing/vad/vad_audio_proc.h index 4a71ce3800..cbdd707129 100644 --- a/modules/audio_processing/vad/vad_audio_proc.h +++ b/modules/audio_processing/vad/vad_audio_proc.h @@ -35,7 +35,7 @@ class VadAudioProc { size_t length, AudioFeatures* audio_features); - static const size_t kDftSize = 512; + static constexpr size_t kDftSize = 512; private: void PitchAnalysis(double* pitch_gains, double* pitch_lags_hz, size_t length); @@ -51,28 +51,24 @@ class VadAudioProc { // For every 30 ms we compute 3 spectral peak there for 3 LPC analysis. // LPC is computed over 15 ms of windowed audio. For every 10 ms sub-frame // we need 5 ms of past signal to create the input of LPC analysis. - enum : size_t { - kNumPastSignalSamples = static_cast(kSampleRateHz / 200) - }; + static constexpr size_t kNumPastSignalSamples = + static_cast(kSampleRateHz / 200); // TODO(turajs): maybe defining this at a higher level (maybe enum) so that // all the code recognize it as "no-error." - enum : int { kNoError = 0 }; - - enum : size_t { kNum10msSubframes = 3 }; - enum : size_t { - kNumSubframeSamples = static_cast(kSampleRateHz / 100) - }; - enum : size_t { - // Samples in 30 ms @ given sampling rate. - kNumSamplesToProcess = kNum10msSubframes * kNumSubframeSamples - }; - enum : size_t { - kBufferLength = kNumPastSignalSamples + kNumSamplesToProcess - }; - enum : size_t { kIpLength = kDftSize >> 1 }; - enum : size_t { kWLength = kDftSize >> 1 }; - enum : size_t { kLpcOrder = 16 }; + static constexpr int kNoError = 0; + + static constexpr size_t kNum10msSubframes = 3; + static constexpr size_t kNumSubframeSamples = + static_cast(kSampleRateHz / 100); + // Samples in 30 ms @ given sampling rate. + static constexpr size_t kNumSamplesToProcess = + size_t{kNum10msSubframes} * kNumSubframeSamples; + static constexpr size_t kBufferLength = + size_t{kNumPastSignalSamples} + kNumSamplesToProcess; + static constexpr size_t kIpLength = kDftSize >> 1; + static constexpr size_t kWLength = kDftSize >> 1; + static constexpr size_t kLpcOrder = 16; size_t ip_[kIpLength]; float w_fft_[kWLength]; diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn index c0b064d9ed..0495efb293 100644 --- a/modules/congestion_controller/BUILD.gn +++ b/modules/congestion_controller/BUILD.gn @@ -33,6 +33,7 @@ rtc_library("congestion_controller") { "../../api/units:data_rate", "../../api/units:time_delta", "../../api/units:timestamp", + "../../rtc_base:logging", "../../rtc_base/synchronization:mutex", "../pacing", "../remote_bitrate_estimator", diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index 9aafedbcb4..00cd2d55c1 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -31,11 +31,11 @@ rtc_library("goog_cc") { ":pushback_controller", ":send_side_bwe", "../..:module_api", + "../../../api:field_trials_view", "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:field_trial_based_config", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:data_size", "../../../api/units:time_delta", @@ -74,8 +74,8 @@ rtc_library("pushback_controller") { "congestion_window_pushback_controller.h", ] deps = [ + "../../../api:field_trials_view", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_size", "../../../rtc_base:checks", "../../../rtc_base/experiments:rate_control_settings", @@ -92,9 +92,9 @@ rtc_library("alr_detector") { "alr_detector.h", ] deps = [ + "../../../api:field_trials_view", "../../../api/rtc_event_log", "../../../api/transport:field_trial_based_config", - "../../../api/transport:webrtc_key_value_config", "../../../logging:rtc_event_pacing", "../../../rtc_base:checks", "../../../rtc_base:safe_conversions", @@ -124,11 +124,13 @@ rtc_library("estimators") { ] deps = [ + "../../../api:field_trials_view", "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", + "../../../api/units:data_size", + "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", @@ -153,14 +155,16 @@ rtc_library("loss_based_bwe_v2") { ] deps = [ "../../../api:array_view", + "../../../api:field_trials_view", + "../../../api:network_state_predictor_api", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:data_size", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:logging", "../../../rtc_base/experiments:field_trial_parser", + "../../remote_bitrate_estimator", ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", @@ -175,8 +179,8 @@ rtc_library("loss_based_bwe_v1") { "loss_based_bandwidth_estimation.h", ] deps = [ + "../../../api:field_trials_view", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -195,9 +199,10 @@ rtc_library("send_side_bwe") { deps = [ ":loss_based_bwe_v1", ":loss_based_bwe_v2", + "../../../api:field_trials_view", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -226,15 +231,16 @@ rtc_library("delay_based_bwe") { deps = [ ":estimators", + "../../../api:field_trials_view", "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:race_checker", "../../../rtc_base/experiments:field_trial_parser", "../../../system_wrappers:metrics", "../../pacing", @@ -253,9 +259,9 @@ rtc_library("probe_controller") { ] deps = [ + "../../../api:field_trials_view", "../../../api/rtc_event_log", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:time_delta", "../../../api/units:timestamp", @@ -325,13 +331,14 @@ if (rtc_include_tests) { ":probe_controller", ":pushback_controller", ":send_side_bwe", + "../../../api:field_trials_view", + "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", "../../../api/test/network_emulation", "../../../api/test/network_emulation:create_cross_traffic", "../../../api/transport:field_trial_based_config", "../../../api/transport:goog_cc", "../../../api/transport:network_control", - "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", "../../../api/units:data_size", "../../../api/units:time_delta", @@ -339,7 +346,8 @@ if (rtc_include_tests) { "../../../logging:mocks", "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:random", "../../../rtc_base:rtc_base_tests_utils", "../../../rtc_base:stringutils", "../../../rtc_base/experiments:alr_experiment", @@ -351,6 +359,7 @@ if (rtc_include_tests) { "../../pacing", "//testing/gmock", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] } } } diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc index f3c992f571..08b42a8168 100644 --- a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.cc @@ -22,7 +22,7 @@ namespace webrtc { AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : AcknowledgedBitrateEstimator( key_value_config, std::make_unique(key_value_config)) {} @@ -30,7 +30,7 @@ AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator( AcknowledgedBitrateEstimator::~AcknowledgedBitrateEstimator() {} AcknowledgedBitrateEstimator::AcknowledgedBitrateEstimator( - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, std::unique_ptr bitrate_estimator) : in_alr_(false), bitrate_estimator_(std::move(bitrate_estimator)) {} diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h index 97dd965fa4..d10846ab3a 100644 --- a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h @@ -15,8 +15,8 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h" #include "modules/congestion_controller/goog_cc/bitrate_estimator.h" @@ -27,11 +27,11 @@ class AcknowledgedBitrateEstimator : public AcknowledgedBitrateEstimatorInterface { public: AcknowledgedBitrateEstimator( - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, std::unique_ptr bitrate_estimator); explicit AcknowledgedBitrateEstimator( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); ~AcknowledgedBitrateEstimator() override; void IncomingPacketFeedbackVector( diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc index d5b1a13fcc..c043353a7a 100644 --- a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.cc @@ -12,6 +12,7 @@ #include +#include "api/units/time_delta.h" #include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator.h" #include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h" #include "rtc_base/logging.h" @@ -21,25 +22,39 @@ namespace webrtc { constexpr char RobustThroughputEstimatorSettings::kKey[]; RobustThroughputEstimatorSettings::RobustThroughputEstimatorSettings( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { Parser()->Parse( key_value_config->Lookup(RobustThroughputEstimatorSettings::kKey)); - if (min_packets < 10 || kMaxPackets < min_packets) { - RTC_LOG(LS_WARNING) << "Window size must be between 10 and " << kMaxPackets - << " packets"; - min_packets = 20; + if (window_packets < 10 || 1000 < window_packets) { + RTC_LOG(LS_WARNING) << "Window size must be between 10 and 1000 packets"; + window_packets = 20; } - if (initial_packets < 10 || kMaxPackets < initial_packets) { - RTC_LOG(LS_WARNING) << "Initial size must be between 10 and " << kMaxPackets - << " packets"; - initial_packets = 20; + if (max_window_packets < 10 || 1000 < max_window_packets) { + RTC_LOG(LS_WARNING) + << "Max window size must be between 10 and 1000 packets"; + max_window_packets = 500; + } + max_window_packets = std::max(max_window_packets, window_packets); + + if (required_packets < 10 || 1000 < required_packets) { + RTC_LOG(LS_WARNING) << "Required number of initial packets must be between " + "10 and 1000 packets"; + required_packets = 10; } - initial_packets = std::min(initial_packets, min_packets); - if (window_duration < TimeDelta::Millis(100) || - TimeDelta::Millis(2000) < window_duration) { - RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 2000 ms"; - window_duration = TimeDelta::Millis(500); + required_packets = std::min(required_packets, window_packets); + + if (min_window_duration < TimeDelta::Millis(100) || + TimeDelta::Millis(3000) < min_window_duration) { + RTC_LOG(LS_WARNING) << "Window duration must be between 100 and 3000 ms"; + min_window_duration = TimeDelta::Millis(750); + } + if (max_window_duration < TimeDelta::Seconds(1) || + TimeDelta::Seconds(15) < max_window_duration) { + RTC_LOG(LS_WARNING) << "Max window duration must be between 1 and 15 s"; + max_window_duration = TimeDelta::Seconds(5); } + min_window_duration = std::min(min_window_duration, max_window_duration); + if (unacked_weight < 0.0 || 1.0 < unacked_weight) { RTC_LOG(LS_WARNING) << "Weight for prior unacked size must be between 0 and 1."; @@ -49,14 +64,14 @@ RobustThroughputEstimatorSettings::RobustThroughputEstimatorSettings( std::unique_ptr RobustThroughputEstimatorSettings::Parser() { - return StructParametersParser::Create("enabled", &enabled, // - "reduce_bias", &reduce_bias, // - "assume_shared_link", // - &assume_shared_link, // - "min_packets", &min_packets, // - "window_duration", &window_duration, // - "initial_packets", &initial_packets, // - "unacked_weight", &unacked_weight); + return StructParametersParser::Create( + "enabled", &enabled, // + "window_packets", &window_packets, // + "max_window_packets", &max_window_packets, // + "window_duration", &min_window_duration, // + "max_window_duration", &max_window_duration, // + "required_packets", &required_packets, // + "unacked_weight", &unacked_weight); } AcknowledgedBitrateEstimatorInterface:: @@ -64,7 +79,7 @@ AcknowledgedBitrateEstimatorInterface:: std::unique_ptr AcknowledgedBitrateEstimatorInterface::Create( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { RobustThroughputEstimatorSettings simplified_estimator_settings( key_value_config); if (simplified_estimator_settings.enabled) { diff --git a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h index b6cee43125..515af1efc9 100644 --- a/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h +++ b/modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h @@ -11,51 +11,56 @@ #ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ACKNOWLEDGED_BITRATE_ESTIMATOR_INTERFACE_H_ +#include + #include #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/experiments/struct_parameters_parser.h" namespace webrtc { struct RobustThroughputEstimatorSettings { static constexpr char kKey[] = "WebRTC-Bwe-RobustThroughputEstimatorSettings"; - static constexpr size_t kMaxPackets = 500; RobustThroughputEstimatorSettings() = delete; explicit RobustThroughputEstimatorSettings( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); bool enabled = false; // Set to true to use RobustThroughputEstimator. - // The estimator handles delay spikes by removing the largest receive time - // gap, but this introduces some bias that may lead to overestimation when - // there isn't any delay spike. If `reduce_bias` is true, we instead replace - // the largest receive time gap by the second largest. This reduces the bias - // at the cost of not completely removing the genuine delay spikes. - bool reduce_bias = true; - - // If `assume_shared_link` is false, we ignore the size of the first packet - // when computing the receive rate. Otherwise, we remove half of the first - // and last packet's sizes. - bool assume_shared_link = false; - - // The estimator window keeps at least `min_packets` packets and up to - // kMaxPackets received during the last `window_duration`. - unsigned min_packets = 20; - TimeDelta window_duration = TimeDelta::Millis(500); - - // The estimator window requires at least `initial_packets` packets received - // over at least `initial_duration`. - unsigned initial_packets = 20; - + // The estimator keeps the smallest window containing at least + // `window_packets` and at least the packets received during the last + // `min_window_duration` milliseconds. + // (This means that it may store more than `window_packets` at high bitrates, + // and a longer duration than `min_window_duration` at low bitrates.) + // However, if will never store more than kMaxPackets (for performance + // reasons), and never longer than max_window_duration (to avoid very old + // packets influencing the estimate for example when sending is paused). + unsigned window_packets = 20; + unsigned max_window_packets = 500; + TimeDelta min_window_duration = TimeDelta::Seconds(1); + TimeDelta max_window_duration = TimeDelta::Seconds(5); + + // The estimator window requires at least `required_packets` packets + // to produce an estimate. + unsigned required_packets = 10; + + // If audio packets aren't included in allocation (i.e. the + // estimated available bandwidth is divided only among the video + // streams), then `unacked_weight` should be set to 0. // If audio packets are included in allocation, but not in bandwidth - // estimation and the sent audio packets get double counted, - // then it might be useful to reduce the weight to 0.5. + // estimation (i.e. they don't have transport-wide sequence numbers, + // but we nevertheless divide the estimated available bandwidth among + // both audio and video streams), then `unacked_weight` should be set to 1. + // If all packets have transport-wide sequence numbers, then the value + // of `unacked_weight` doesn't matter. double unacked_weight = 1.0; std::unique_ptr Parser(); @@ -64,7 +69,7 @@ struct RobustThroughputEstimatorSettings { class AcknowledgedBitrateEstimatorInterface { public: static std::unique_ptr Create( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); virtual ~AcknowledgedBitrateEstimatorInterface(); virtual void IncomingPacketFeedbackVector( diff --git a/modules/congestion_controller/goog_cc/alr_detector.cc b/modules/congestion_controller/goog_cc/alr_detector.cc index 6a62954c36..f1e649b7cd 100644 --- a/modules/congestion_controller/goog_cc/alr_detector.cc +++ b/modules/congestion_controller/goog_cc/alr_detector.cc @@ -24,8 +24,7 @@ namespace webrtc { namespace { -AlrDetectorConfig GetConfigFromTrials( - const WebRtcKeyValueConfig* key_value_config) { +AlrDetectorConfig GetConfigFromTrials(const FieldTrialsView* key_value_config) { RTC_CHECK(AlrExperimentSettings::MaxOneFieldTrialEnabled(*key_value_config)); absl::optional experiment_settings = AlrExperimentSettings::CreateFromFieldTrial( @@ -61,10 +60,10 @@ std::unique_ptr AlrDetectorConfig::Parser() { AlrDetector::AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log) : conf_(config), alr_budget_(0, true), event_log_(event_log) {} -AlrDetector::AlrDetector(const WebRtcKeyValueConfig* key_value_config) +AlrDetector::AlrDetector(const FieldTrialsView* key_value_config) : AlrDetector(GetConfigFromTrials(key_value_config), nullptr) {} -AlrDetector::AlrDetector(const WebRtcKeyValueConfig* key_value_config, +AlrDetector::AlrDetector(const FieldTrialsView* key_value_config, RtcEventLog* event_log) : AlrDetector(GetConfigFromTrials(key_value_config), event_log) {} AlrDetector::~AlrDetector() {} diff --git a/modules/congestion_controller/goog_cc/alr_detector.h b/modules/congestion_controller/goog_cc/alr_detector.h index ee3fe92845..5e7a3e1075 100644 --- a/modules/congestion_controller/goog_cc/alr_detector.h +++ b/modules/congestion_controller/goog_cc/alr_detector.h @@ -13,10 +13,11 @@ #include #include + #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "modules/pacing/interval_budget.h" #include "rtc_base/experiments/alr_experiment.h" #include "rtc_base/experiments/struct_parameters_parser.h" @@ -46,9 +47,8 @@ struct AlrDetectorConfig { class AlrDetector { public: AlrDetector(AlrDetectorConfig config, RtcEventLog* event_log); - explicit AlrDetector(const WebRtcKeyValueConfig* key_value_config); - AlrDetector(const WebRtcKeyValueConfig* key_value_config, - RtcEventLog* event_log); + explicit AlrDetector(const FieldTrialsView* key_value_config); + AlrDetector(const FieldTrialsView* key_value_config, RtcEventLog* event_log); ~AlrDetector(); void OnBytesSent(size_t bytes_sent, int64_t send_time_ms); diff --git a/modules/congestion_controller/goog_cc/bitrate_estimator.cc b/modules/congestion_controller/goog_cc/bitrate_estimator.cc index 09b214a798..9c68e48886 100644 --- a/modules/congestion_controller/goog_cc/bitrate_estimator.cc +++ b/modules/congestion_controller/goog_cc/bitrate_estimator.cc @@ -32,7 +32,7 @@ const char kBweThroughputWindowConfig[] = "WebRTC-BweThroughputWindowConfig"; } // namespace -BitrateEstimator::BitrateEstimator(const WebRtcKeyValueConfig* key_value_config) +BitrateEstimator::BitrateEstimator(const FieldTrialsView* key_value_config) : sum_(0), initial_window_ms_("initial_window_ms", kInitialRateWindowMs, diff --git a/modules/congestion_controller/goog_cc/bitrate_estimator.h b/modules/congestion_controller/goog_cc/bitrate_estimator.h index 34114f017c..a6f985800e 100644 --- a/modules/congestion_controller/goog_cc/bitrate_estimator.h +++ b/modules/congestion_controller/goog_cc/bitrate_estimator.h @@ -14,7 +14,7 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/units/data_rate.h" #include "api/units/timestamp.h" #include "rtc_base/experiments/field_trial_parser.h" @@ -28,7 +28,7 @@ namespace webrtc { // unrelated to congestion. class BitrateEstimator { public: - explicit BitrateEstimator(const WebRtcKeyValueConfig* key_value_config); + explicit BitrateEstimator(const FieldTrialsView* key_value_config); virtual ~BitrateEstimator(); virtual void Update(Timestamp at_time, DataSize amount, bool in_alr); diff --git a/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc b/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc index ec642823df..2f188f30ca 100644 --- a/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc +++ b/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.cc @@ -23,7 +23,7 @@ namespace webrtc { CongestionWindowPushbackController::CongestionWindowPushbackController( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : add_pacing_( absl::StartsWith(key_value_config->Lookup( "WebRTC-AddPacingToCongestionWindowPushback"), diff --git a/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h b/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h index 7a49a83d5b..ea9ed97c3d 100644 --- a/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h +++ b/modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h @@ -15,7 +15,7 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/units/data_size.h" namespace webrtc { @@ -28,7 +28,7 @@ namespace webrtc { class CongestionWindowPushbackController { public: explicit CongestionWindowPushbackController( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); void UpdateOutstandingData(int64_t outstanding_bytes); void UpdatePacingQueue(int64_t pacing_bytes); uint32_t UpdateTargetBitrate(uint32_t bitrate_bps); diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.cc b/modules/congestion_controller/goog_cc/delay_based_bwe.cc index 2ae5441ef4..750c5c60fb 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.cc @@ -42,7 +42,7 @@ constexpr uint32_t kFixedSsrc = 0; constexpr char BweSeparateAudioPacketsSettings::kKey[]; BweSeparateAudioPacketsSettings::BweSeparateAudioPacketsSettings( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { Parser()->Parse( key_value_config->Lookup(BweSeparateAudioPacketsSettings::kKey)); } @@ -62,7 +62,7 @@ DelayBasedBwe::Result::Result() recovered_from_overuse(false), backoff_in_alr(false) {} -DelayBasedBwe::DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config, +DelayBasedBwe::DelayBasedBwe(const FieldTrialsView* key_value_config, RtcEventLog* event_log, NetworkStatePredictor* network_state_predictor) : event_log_(event_log), @@ -181,23 +181,23 @@ void DelayBasedBwe::IncomingPacketFeedback(const PacketResult& packet_feedback, } DataSize packet_size = packet_feedback.sent_packet.size; - TimeDelta send_delta = TimeDelta::Zero(); - TimeDelta recv_delta = TimeDelta::Zero(); - int size_delta = 0; - - InterArrivalDelta* inter_arrival_for_packet = - (separate_audio_.enabled && packet_feedback.sent_packet.audio) - ? video_inter_arrival_delta_.get() - : audio_inter_arrival_delta_.get(); - bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( - packet_feedback.sent_packet.send_time, packet_feedback.receive_time, - at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); - - delay_detector_for_packet->Update( - recv_delta.ms(), send_delta.ms(), - packet_feedback.sent_packet.send_time.ms(), - packet_feedback.receive_time.ms(), packet_size.bytes(), - calculated_deltas); + TimeDelta send_delta = TimeDelta::Zero(); + TimeDelta recv_delta = TimeDelta::Zero(); + int size_delta = 0; + + InterArrivalDelta* inter_arrival_for_packet = + (separate_audio_.enabled && packet_feedback.sent_packet.audio) + ? video_inter_arrival_delta_.get() + : audio_inter_arrival_delta_.get(); + bool calculated_deltas = inter_arrival_for_packet->ComputeDeltas( + packet_feedback.sent_packet.send_time, packet_feedback.receive_time, + at_time, packet_size.bytes(), &send_delta, &recv_delta, &size_delta); + + delay_detector_for_packet->Update(recv_delta.ms(), + send_delta.ms(), + packet_feedback.sent_packet.send_time.ms(), + packet_feedback.receive_time.ms(), + packet_size.bytes(), calculated_deltas); } DataRate DelayBasedBwe::TriggerOveruse(Timestamp at_time, @@ -243,8 +243,8 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( if (probe_bitrate) { result.probe = true; result.updated = true; - result.target_bitrate = *probe_bitrate; rate_control_.SetEstimate(*probe_bitrate, at_time); + result.target_bitrate = rate_control_.LatestEstimate(); } else { result.updated = UpdateEstimate(at_time, acked_bitrate, &result.target_bitrate); @@ -266,6 +266,8 @@ DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate( prev_bitrate_ = bitrate; prev_state_ = detector_state; } + + result.delay_detector_state = detector_state; return result; } diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe.h b/modules/congestion_controller/goog_cc/delay_based_bwe.h index 7823f77abe..dd4f7d84a4 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe.h @@ -18,9 +18,9 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/network_state_predictor.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h" #include "modules/congestion_controller/goog_cc/inter_arrival_delta.h" #include "modules/congestion_controller/goog_cc/probe_bitrate_estimator.h" @@ -37,7 +37,7 @@ struct BweSeparateAudioPacketsSettings { BweSeparateAudioPacketsSettings() = default; explicit BweSeparateAudioPacketsSettings( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); bool enabled = false; int packet_threshold = 10; @@ -56,9 +56,10 @@ class DelayBasedBwe { DataRate target_bitrate = DataRate::Zero(); bool recovered_from_overuse; bool backoff_in_alr; + BandwidthUsage delay_detector_state; }; - explicit DelayBasedBwe(const WebRtcKeyValueConfig* key_value_config, + explicit DelayBasedBwe(const FieldTrialsView* key_value_config, RtcEventLog* event_log, NetworkStatePredictor* network_state_predictor); @@ -103,7 +104,7 @@ class DelayBasedBwe { rtc::RaceChecker network_race_; RtcEventLog* const event_log_; - const WebRtcKeyValueConfig* const key_value_config_; + const FieldTrialsView* const key_value_config_; // Alternatively, run two separate overuse detectors for audio and video, // and fall back to the audio one if we haven't seen a video packet in a diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc index 71b7ee7f90..b7dc6aae47 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest.cc @@ -214,6 +214,41 @@ TEST_F(DelayBasedBweTest, TestInitialOveruse) { 15000); } +TEST_F(DelayBasedBweTest, TestTimestampPrecisionHandling) { + // This test does some basic checks to make sure that timestamps with higher + // than millisecond precision are handled properly and do not cause any + // problems in the estimator. Specifically, previously reported in + // webrtc:14023 and described in more details there, the rounding to the + // nearest milliseconds caused discrepancy in the accumulated delay. This lead + // to false-positive overuse detection. + // Technical details of the test: + // Send times(ms): 0.000, 9.725, 20.000, 29.725, 40.000, 49.725, ... + // Recv times(ms): 0.500, 10.000, 20.500, 30.000, 40.500, 50.000, ... + // Send deltas(ms): 9.750, 10.250, 9.750, 10.250, 9.750, ... + // Recv deltas(ms): 9.500, 10.500, 9.500, 10.500, 9.500, ... + // There is no delay building up between the send times and the receive times, + // therefore this case should never lead to an overuse detection. However, if + // the time deltas were accidentally rounded to the nearest milliseconds, then + // all the send deltas would be equal to 10ms while some recv deltas would + // round up to 11ms which would lead in a false illusion of delay build up. + uint32_t last_bitrate = bitrate_observer_.latest_bitrate(); + for (int i = 0; i < 1000; ++i) { + clock_.AdvanceTimeMicroseconds(500); + IncomingFeedback(clock_.CurrentTime(), + clock_.CurrentTime() - TimeDelta::Micros(500), 1000, + PacedPacketInfo()); + clock_.AdvanceTimeMicroseconds(9500); + IncomingFeedback(clock_.CurrentTime(), + clock_.CurrentTime() - TimeDelta::Micros(250), 1000, + PacedPacketInfo()); + clock_.AdvanceTimeMicroseconds(10000); + + // The bitrate should never decrease in this test. + EXPECT_LE(last_bitrate, bitrate_observer_.latest_bitrate()); + last_bitrate = bitrate_observer_.latest_bitrate(); + } +} + class DelayBasedBweTestWithBackoffTimeoutExperiment : public DelayBasedBweTest { public: DelayBasedBweTestWithBackoffTimeoutExperiment() diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc index 3eb0ae38e5..bb91d5957d 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.cc @@ -128,7 +128,6 @@ int64_t StreamGenerator::GenerateFrame(std::vector* packets, auto it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); (*it)->GenerateFrame(time_now_us, packets); - int i = 0; for (PacketResult& packet : *packets) { int capacity_bpus = capacity_ / 1000; int64_t required_network_time_us = @@ -138,7 +137,6 @@ int64_t StreamGenerator::GenerateFrame(std::vector* packets, std::max(time_now_us + required_network_time_us, prev_arrival_time_us_ + required_network_time_us); packet.receive_time = Timestamp::Micros(prev_arrival_time_us_); - ++i; } it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); return std::max((*it)->next_rtp_time(), time_now_us); @@ -181,10 +179,17 @@ void DelayBasedBweTest::IncomingFeedback(int64_t arrival_time_ms, size_t payload_size, const PacedPacketInfo& pacing_info) { RTC_CHECK_GE(arrival_time_ms + arrival_time_offset_ms_, 0); + IncomingFeedback(Timestamp::Millis(arrival_time_ms + arrival_time_offset_ms_), + Timestamp::Millis(send_time_ms), payload_size, pacing_info); +} + +void DelayBasedBweTest::IncomingFeedback(Timestamp receive_time, + Timestamp send_time, + size_t payload_size, + const PacedPacketInfo& pacing_info) { PacketResult packet; - packet.receive_time = - Timestamp::Millis(arrival_time_ms + arrival_time_offset_ms_); - packet.sent_packet.send_time = Timestamp::Millis(send_time_ms); + packet.receive_time = receive_time; + packet.sent_packet.send_time = send_time; packet.sent_packet.size = DataSize::Bytes(payload_size); packet.sent_packet.pacing_info = pacing_info; if (packet.sent_packet.pacing_info.probe_cluster_id != diff --git a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h index 474d2970df..a5da4a017d 100644 --- a/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h +++ b/modules/congestion_controller/goog_cc/delay_based_bwe_unittest_helper.h @@ -131,6 +131,10 @@ class DelayBasedBweTest : public ::testing::Test { int64_t send_time_ms, size_t payload_size, const PacedPacketInfo& pacing_info); + void IncomingFeedback(Timestamp receive_time, + Timestamp send_time, + size_t payload_size, + const PacedPacketInfo& pacing_info); // Generates a frame of packets belonging to a stream at a given bitrate and // with a given ssrc. The stream is pushed through a very simple simulated diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index ba656be234..5e85c9fe7a 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -60,11 +60,11 @@ int64_t GetBpsOrDefault(const absl::optional& rate, } } -bool IsEnabled(const WebRtcKeyValueConfig* config, absl::string_view key) { +bool IsEnabled(const FieldTrialsView* config, absl::string_view key) { return absl::StartsWith(config->Lookup(key), "Enabled"); } -bool IsNotDisabled(const WebRtcKeyValueConfig* config, absl::string_view key) { +bool IsNotDisabled(const FieldTrialsView* config, absl::string_view key) { return !absl::StartsWith(config->Lookup(key), "Disabled"); } } // namespace @@ -92,6 +92,8 @@ GoogCcNetworkController::GoogCcNetworkController(NetworkControllerConfig config, pace_at_max_of_bwe_and_lower_link_capacity_( IsEnabled(key_value_config_, "WebRTC-Bwe-PaceAtMaxOfBweAndLowerLinkCapacity")), + pace_at_loss_based_bwe_when_loss_( + IsEnabled(key_value_config_, "WebRTC-Bwe-PaceAtLossBaseBweWhenLoss")), probe_controller_( new ProbeController(key_value_config_, config.event_log)), congestion_window_pushback_controller_( @@ -495,7 +497,6 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, report.feedback_time); - bandwidth_estimation_->IncomingPacketFeedbackVector(report); for (const auto& feedback : report.SortedByReceiveTime()) { if (feedback.sent_packet.pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { @@ -553,11 +554,13 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( } // Since SetSendBitrate now resets the delay-based estimate, we have to // call UpdateDelayBasedEstimate after SetSendBitrate. - bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time, - result.target_bitrate); + bandwidth_estimation_->UpdateDelayBasedEstimate( + report.feedback_time, result.target_bitrate, + result.delay_detector_state); // Update the estimate in the ProbeController, in case we want to probe. MaybeTriggerOnNetworkChanged(&update, report.feedback_time); } + bandwidth_estimation_->UpdateLossBasedEstimatorFromFeedbackVector(report); recovered_from_overuse = result.recovered_from_overuse; backoff_in_alr = result.backoff_in_alr; @@ -699,7 +702,10 @@ PacerConfig GoogCcNetworkController::GetPacingRates(Timestamp at_time) const { // Pacing rate is based on target rate before congestion window pushback, // because we don't want to build queues in the pacer when pushback occurs. DataRate pacing_rate = DataRate::Zero(); - if (pace_at_max_of_bwe_and_lower_link_capacity_ && estimate_) { + if ((pace_at_max_of_bwe_and_lower_link_capacity_ || + (pace_at_loss_based_bwe_when_loss_ && + last_loss_based_target_rate_ >= delay_based_bwe_->last_estimate())) && + estimate_) { pacing_rate = std::max({min_total_allocated_bitrate_, estimate_->link_capacity_lower, last_loss_based_target_rate_}) * diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/modules/congestion_controller/goog_cc/goog_cc_network_control.h index 946c076939..884b572740 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.h +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.h @@ -18,12 +18,12 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/field_trial_based_config.h" #include "api/transport/network_control.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/timestamp.h" @@ -84,7 +84,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { PacerConfig GetPacingRates(Timestamp at_time) const; const FieldTrialBasedConfig trial_based_config_; - const WebRtcKeyValueConfig* const key_value_config_; + const FieldTrialsView* const key_value_config_; RtcEventLog* const event_log_; const bool packet_feedback_only_; FieldTrialFlag safe_reset_on_route_change_; @@ -95,6 +95,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { const RateControlSettings rate_control_settings_; const bool loss_based_stable_rate_; const bool pace_at_max_of_bwe_and_lower_link_capacity_; + const bool pace_at_loss_based_bwe_when_loss_; const std::unique_ptr probe_controller_; const std::unique_ptr diff --git a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc index 33974dc900..7524c84d92 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.cc @@ -75,7 +75,7 @@ double ExponentialUpdate(TimeDelta window, TimeDelta interval) { return 1.0f - exp(interval / window * -1.0); } -bool IsEnabled(const webrtc::WebRtcKeyValueConfig& key_value_config, +bool IsEnabled(const webrtc::FieldTrialsView& key_value_config, absl::string_view name) { return absl::StartsWith(key_value_config.Lookup(name), "Enabled"); } @@ -83,7 +83,7 @@ bool IsEnabled(const webrtc::WebRtcKeyValueConfig& key_value_config, } // namespace LossBasedControlConfig::LossBasedControlConfig( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : enabled(IsEnabled(*key_value_config, kBweLossBasedControl)), min_increase_factor("min_incr", 1.02), max_increase_factor("max_incr", 1.08), @@ -118,7 +118,7 @@ LossBasedControlConfig::LossBasedControlConfig(const LossBasedControlConfig&) = LossBasedControlConfig::~LossBasedControlConfig() = default; LossBasedBandwidthEstimation::LossBasedBandwidthEstimation( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : config_(key_value_config), average_loss_(0), average_loss_max_(0), diff --git a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h index 20ff092e6f..9f69caba89 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h @@ -13,8 +13,8 @@ #include +#include "api/field_trials_view.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -23,7 +23,7 @@ namespace webrtc { struct LossBasedControlConfig { - explicit LossBasedControlConfig(const WebRtcKeyValueConfig* key_value_config); + explicit LossBasedControlConfig(const FieldTrialsView* key_value_config); LossBasedControlConfig(const LossBasedControlConfig&); LossBasedControlConfig& operator=(const LossBasedControlConfig&) = default; ~LossBasedControlConfig(); @@ -52,7 +52,7 @@ struct LossBasedControlConfig { class LossBasedBandwidthEstimation { public: explicit LossBasedBandwidthEstimation( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); // Returns the new estimate. DataRate Update(Timestamp at_time, DataRate min_bitrate, diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc index 44041143bf..0d19e567e8 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -21,12 +21,14 @@ #include "absl/algorithm/container.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/field_trials_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" @@ -89,25 +91,18 @@ double GetLossProbability(double inherent_loss, << ToString(loss_limited_bandwidth); } - // We approximate the loss model - // loss_probability = inherent_loss + (1 - inherent_loss) * - // max(0, sending_rate - bandwidth) / sending_rate - // by - // loss_probability = inherent_loss + - // max(0, sending_rate - bandwidth) / sending_rate - // as it allows for simpler calculations and makes little difference in - // practice. double loss_probability = inherent_loss; if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) && (sending_rate > loss_limited_bandwidth)) { - loss_probability += (sending_rate - loss_limited_bandwidth) / sending_rate; + loss_probability += (1 - inherent_loss) * + (sending_rate - loss_limited_bandwidth) / sending_rate; } return std::min(std::max(loss_probability, 1.0e-6), 1.0 - 1.0e-6); } } // namespace -LossBasedBweV2::LossBasedBweV2(const WebRtcKeyValueConfig* key_value_config) +LossBasedBweV2::LossBasedBweV2(const FieldTrialsView* key_value_config) : config_(CreateConfig(key_value_config)) { if (!config_.has_value()) { RTC_LOG(LS_VERBOSE) << "The configuration does not specify that the " @@ -138,7 +133,8 @@ bool LossBasedBweV2::IsReady() const { num_observations_ > 0; } -DataRate LossBasedBweV2::GetBandwidthEstimate() const { +DataRate LossBasedBweV2::GetBandwidthEstimate( + DataRate delay_based_limit) const { if (!IsReady()) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) @@ -156,8 +152,14 @@ DataRate LossBasedBweV2::GetBandwidthEstimate() const { return DataRate::PlusInfinity(); } - return std::min(current_estimate_.loss_limited_bandwidth, - GetInstantUpperBound()); + if (delay_based_limit.IsFinite()) { + return std::min({current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound(), + delay_based_limit * config_->delay_based_limit_factor}); + } else { + return std::min(current_estimate_.loss_limited_bandwidth, + GetInstantUpperBound()); + } } void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { @@ -180,7 +182,8 @@ void LossBasedBweV2::SetBandwidthEstimate(DataRate bandwidth_estimate) { void LossBasedBweV2::UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate) { + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state) { if (!IsEnabled()) { RTC_LOG(LS_WARNING) << "The estimator must be enabled before it can be used."; @@ -192,7 +195,7 @@ void LossBasedBweV2::UpdateBandwidthEstimate( return; } - if (!PushBackObservation(packet_results)) { + if (!PushBackObservation(packet_results, delay_detector_state)) { return; } @@ -217,13 +220,41 @@ void LossBasedBweV2::UpdateBandwidthEstimate( current_estimate_.loss_limited_bandwidth) { last_time_estimate_reduced_ = last_send_time_most_recent_observation_; } + + // Bound the estimate increasement if: + // 1. The estimate is limited due to loss, and + // 2. The estimate has been increased for less than `delayed_increase_window` + // ago, and + // 3. The best candidate is greater than bandwidth_limit_in_current_window. + if (limited_due_to_loss_candidate_ && + recovering_after_loss_timestamp_.IsFinite() && + recovering_after_loss_timestamp_ + config_->delayed_increase_window > + last_send_time_most_recent_observation_ && + best_candidate.loss_limited_bandwidth > + bandwidth_limit_in_current_window_) { + best_candidate.loss_limited_bandwidth = bandwidth_limit_in_current_window_; + } + limited_due_to_loss_candidate_ = + delay_based_estimate.IsFinite() && + best_candidate.loss_limited_bandwidth < delay_based_estimate; + + if (limited_due_to_loss_candidate_ && + (recovering_after_loss_timestamp_.IsInfinite() || + recovering_after_loss_timestamp_ + config_->delayed_increase_window < + last_send_time_most_recent_observation_)) { + bandwidth_limit_in_current_window_ = std::max( + congestion_controller::GetMinBitrate(), + best_candidate.loss_limited_bandwidth * config_->max_increase_factor); + recovering_after_loss_timestamp_ = last_send_time_most_recent_observation_; + } + current_estimate_ = best_candidate; } // Returns a `LossBasedBweV2::Config` iff the `key_value_config` specifies a // configuration for the `LossBasedBweV2` which is explicitly enabled. absl::optional LossBasedBweV2::CreateConfig( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { FieldTrialParameter enabled("Enabled", false); FieldTrialParameter bandwidth_rampup_upper_bound_factor( "BwRampupUpperBoundFactor", 1.1); @@ -264,6 +295,18 @@ absl::optional LossBasedBweV2::CreateConfig( "InstantUpperBoundLossOffset", 0.05); FieldTrialParameter temporal_weight_factor("TemporalWeightFactor", 0.99); + FieldTrialParameter bandwidth_backoff_lower_bound_factor( + "BwBackoffLowerBoundFactor", 1.0); + FieldTrialParameter trendline_integration_enabled( + "TrendlineIntegrationEnabled", false); + FieldTrialParameter delay_based_limit_factor("DelayBasedLimitFactor", + 1.0); + FieldTrialParameter trendline_window_size("TrendlineWindowSize", 20); + FieldTrialParameter max_increase_factor("MaxIncreaseFactor", 1000.0); + FieldTrialParameter delayed_increase_window( + "DelayedIncreaseWindow", TimeDelta::Millis(300)); + FieldTrialParameter use_acked_bitrate_only_when_overusing( + "UseAckedBitrateOnlyWhenOverusing", false); if (key_value_config) { ParseFieldTrial({&enabled, @@ -287,7 +330,14 @@ absl::optional LossBasedBweV2::CreateConfig( &instant_upper_bound_temporal_weight_factor, &instant_upper_bound_bandwidth_balance, &instant_upper_bound_loss_offset, - &temporal_weight_factor}, + &temporal_weight_factor, + &bandwidth_backoff_lower_bound_factor, + &trendline_integration_enabled, + &delay_based_limit_factor, + &trendline_window_size, + &max_increase_factor, + &delayed_increase_window, + &use_acked_bitrate_only_when_overusing}, key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); } @@ -328,6 +378,15 @@ absl::optional LossBasedBweV2::CreateConfig( config->instant_upper_bound_loss_offset = instant_upper_bound_loss_offset.Get(); config->temporal_weight_factor = temporal_weight_factor.Get(); + config->bandwidth_backoff_lower_bound_factor = + bandwidth_backoff_lower_bound_factor.Get(); + config->trendline_integration_enabled = trendline_integration_enabled.Get(); + config->delay_based_limit_factor = delay_based_limit_factor.Get(); + config->trendline_window_size = trendline_window_size.Get(); + config->max_increase_factor = max_increase_factor.Get(); + config->delayed_increase_window = delayed_increase_window.Get(); + config->use_acked_bitrate_only_when_overusing = + use_acked_bitrate_only_when_overusing.Get(); return config; } @@ -470,7 +529,34 @@ bool LossBasedBweV2::IsConfigValid() const { << config_->temporal_weight_factor; valid = false; } - + if (config_->bandwidth_backoff_lower_bound_factor > 1.0) { + RTC_LOG(LS_WARNING) + << "The bandwidth backoff lower bound factor must not be greater than " + "1: " + << config_->bandwidth_backoff_lower_bound_factor; + valid = false; + } + if (config_->delay_based_limit_factor < 1.0) { + RTC_LOG(LS_WARNING) + << "The delay based limit factor must not be less than 1: " + << config_->delay_based_limit_factor; + valid = false; + } + if (config_->trendline_window_size < 2) { + RTC_LOG(LS_WARNING) << "The trendline window size must be at least 2: " + << config_->trendline_window_size; + valid = false; + } + if (config_->max_increase_factor <= 0.0) { + RTC_LOG(LS_WARNING) << "The maximum increase factor must be positive: " + << config_->max_increase_factor; + valid = false; + } + if (config_->delayed_increase_window <= TimeDelta::Zero()) { + RTC_LOG(LS_WARNING) << "The delayed increase window must be positive: " + << config_->delayed_increase_window.ms(); + valid = false; + } return valid; } @@ -496,12 +582,32 @@ double LossBasedBweV2::GetAverageReportedLossRatio() const { return static_cast(num_lost_packets) / num_packets; } -DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { +DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound( + DataRate delay_based_estimate) const { + DataRate candidate_bandwidth_upper_bound = DataRate::PlusInfinity(); + if (limited_due_to_loss_candidate_) { + candidate_bandwidth_upper_bound = bandwidth_limit_in_current_window_; + } + + if (config_->trendline_integration_enabled) { + candidate_bandwidth_upper_bound = + std::min(GetInstantUpperBound(), candidate_bandwidth_upper_bound); + if (IsValid(delay_based_estimate)) { + candidate_bandwidth_upper_bound = + std::min(delay_based_estimate, candidate_bandwidth_upper_bound); + } + } + if (!acknowledged_bitrate_.has_value()) - return DataRate::PlusInfinity(); + return candidate_bandwidth_upper_bound; - DataRate candidate_bandwidth_upper_bound = - config_->bandwidth_rampup_upper_bound_factor * (*acknowledged_bitrate_); + candidate_bandwidth_upper_bound = + IsValid(candidate_bandwidth_upper_bound) + ? std::min(candidate_bandwidth_upper_bound, + config_->bandwidth_rampup_upper_bound_factor * + (*acknowledged_bitrate_)) + : config_->bandwidth_rampup_upper_bound_factor * + (*acknowledged_bitrate_); if (config_->rampup_acceleration_max_factor > 0.0) { const TimeDelta time_since_bandwidth_reduced = std::min( @@ -521,31 +627,45 @@ DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { std::vector LossBasedBweV2::GetCandidates( DataRate delay_based_estimate) const { std::vector bandwidths; + bool can_increase_bitrate = TrendlineEsimateAllowBitrateIncrease(); for (double candidate_factor : config_->candidate_factors) { + if (!can_increase_bitrate && candidate_factor > 1.0) { + continue; + } bandwidths.push_back(candidate_factor * current_estimate_.loss_limited_bandwidth); } if (acknowledged_bitrate_.has_value() && - config_->append_acknowledged_rate_candidate) { - bandwidths.push_back(*acknowledged_bitrate_); + config_->append_acknowledged_rate_candidate && + TrendlineEsimateAllowEmergencyBackoff()) { + bandwidths.push_back(*acknowledged_bitrate_ * + config_->bandwidth_backoff_lower_bound_factor); } if (IsValid(delay_based_estimate) && config_->append_delay_based_estimate_candidate) { - bandwidths.push_back(delay_based_estimate); + if (can_increase_bitrate && + delay_based_estimate > current_estimate_.loss_limited_bandwidth) { + bandwidths.push_back(delay_based_estimate); + } } const DataRate candidate_bandwidth_upper_bound = - GetCandidateBandwidthUpperBound(); + GetCandidateBandwidthUpperBound(delay_based_estimate); std::vector candidates; candidates.resize(bandwidths.size()); for (size_t i = 0; i < bandwidths.size(); ++i) { ChannelParameters candidate = current_estimate_; - candidate.loss_limited_bandwidth = std::min( - bandwidths[i], std::max(current_estimate_.loss_limited_bandwidth, - candidate_bandwidth_upper_bound)); + if (config_->trendline_integration_enabled) { + candidate.loss_limited_bandwidth = + std::min(bandwidths[i], candidate_bandwidth_upper_bound); + } else { + candidate.loss_limited_bandwidth = std::min( + bandwidths[i], std::max(current_estimate_.loss_limited_bandwidth, + candidate_bandwidth_upper_bound)); + } candidate.inherent_loss = GetFeasibleInherentLoss(candidate); candidates[i] = candidate; } @@ -704,8 +824,47 @@ void LossBasedBweV2::NewtonsMethodUpdate( } } +bool LossBasedBweV2::TrendlineEsimateAllowBitrateIncrease() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing || + detector_state == BandwidthUsage::kBwUnderusing) { + return false; + } + } + return true; +} + +bool LossBasedBweV2::TrendlineEsimateAllowEmergencyBackoff() const { + if (!config_->trendline_integration_enabled) { + return true; + } + + if (!config_->use_acked_bitrate_only_when_overusing) { + return true; + } + + for (const auto& detector_state : delay_detector_states_) { + if (detector_state == BandwidthUsage::kBwOverusing) { + return true; + } + } + + return false; +} + bool LossBasedBweV2::PushBackObservation( - rtc::ArrayView packet_results) { + rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state) { + delay_detector_states_.push_front(delay_detector_state); + if (static_cast(delay_detector_states_.size()) > + config_->trendline_window_size) { + delay_detector_states_.pop_back(); + } + if (packet_results.empty()) { return false; } @@ -727,9 +886,11 @@ bool LossBasedBweV2::PushBackObservation( const Timestamp last_send_time = packet_results_summary.last_send_time; const TimeDelta observation_duration = last_send_time - last_send_time_most_recent_observation_; - // Too small to be meaningful. - if (observation_duration < config_->observation_duration_lower_bound) { + if (observation_duration <= TimeDelta::Zero() || + (observation_duration < config_->observation_duration_lower_bound && + (delay_detector_state != BandwidthUsage::kBwOverusing || + !config_->trendline_integration_enabled))) { return false; } diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h index f764892b55..9db711250b 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -12,12 +12,14 @@ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ #include +#include #include #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/field_trials_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" @@ -29,7 +31,7 @@ class LossBasedBweV2 { public: // Creates a disabled `LossBasedBweV2` if the // `key_value_config` is not valid. - explicit LossBasedBweV2(const WebRtcKeyValueConfig* key_value_config); + explicit LossBasedBweV2(const FieldTrialsView* key_value_config); LossBasedBweV2(const LossBasedBweV2&) = delete; LossBasedBweV2& operator=(const LossBasedBweV2&) = delete; @@ -42,14 +44,15 @@ class LossBasedBweV2 { bool IsReady() const; // Returns `DataRate::PlusInfinity` if no BWE can be calculated. - DataRate GetBandwidthEstimate() const; + DataRate GetBandwidthEstimate(DataRate delay_based_limit) const; void SetAcknowledgedBitrate(DataRate acknowledged_bitrate); void SetBandwidthEstimate(DataRate bandwidth_estimate); void UpdateBandwidthEstimate( rtc::ArrayView packet_results, - DataRate delay_based_estimate); + DataRate delay_based_estimate, + BandwidthUsage delay_detector_state); private: struct ChannelParameters { @@ -80,6 +83,13 @@ class LossBasedBweV2 { DataRate instant_upper_bound_bandwidth_balance = DataRate::MinusInfinity(); double instant_upper_bound_loss_offset = 0.0; double temporal_weight_factor = 0.0; + double bandwidth_backoff_lower_bound_factor = 0.0; + bool trendline_integration_enabled = false; + double delay_based_limit_factor = 1.0; + int trendline_window_size = 0; + double max_increase_factor = 0.0; + TimeDelta delayed_increase_window = TimeDelta::Zero(); + bool use_acked_bitrate_only_when_overusing = false; }; struct Derivatives { @@ -104,14 +114,14 @@ class LossBasedBweV2 { }; static absl::optional CreateConfig( - const WebRtcKeyValueConfig* key_value_config); + const FieldTrialsView* key_value_config); bool IsConfigValid() const; // Returns `0.0` if not enough loss statistics have been received. double GetAverageReportedLossRatio() const; std::vector GetCandidates( DataRate delay_based_estimate) const; - DataRate GetCandidateBandwidthUpperBound() const; + DataRate GetCandidateBandwidthUpperBound(DataRate delay_based_estimate) const; Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const; double GetFeasibleInherentLoss( const ChannelParameters& channel_parameters) const; @@ -125,8 +135,20 @@ class LossBasedBweV2 { void CalculateTemporalWeights(); void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; + // Returns false if there exists a kBwOverusing or kBwUnderusing in the + // window. + bool TrendlineEsimateAllowBitrateIncrease() const; + + // Returns true if there exists an overusing state in the window. + bool TrendlineEsimateAllowEmergencyBackoff() const; + // Returns false if no observation was created. - bool PushBackObservation(rtc::ArrayView packet_results); + bool PushBackObservation(rtc::ArrayView packet_results, + BandwidthUsage delay_detector_state); + void UpdateTrendlineEstimator( + const std::vector& packet_feedbacks, + Timestamp at_time); + void UpdateDelayDetector(BandwidthUsage delay_detector_state); absl::optional acknowledged_bitrate_; absl::optional config_; @@ -139,6 +161,10 @@ class LossBasedBweV2 { absl::optional cached_instant_upper_bound_; std::vector instant_upper_bound_temporal_weights_; std::vector temporal_weights_; + std::deque delay_detector_states_; + Timestamp recovering_after_loss_timestamp_ = Timestamp::MinusInfinity(); + DataRate bandwidth_limit_in_current_window_ = DataRate::PlusInfinity(); + bool limited_due_to_loss_candidate_ = false; }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc index 05334885f2..8e002b4bd6 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc @@ -11,7 +11,9 @@ #include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" #include +#include +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" @@ -28,69 +30,132 @@ namespace { using ::webrtc::test::ExplicitKeyValueConfig; constexpr TimeDelta kObservationDurationLowerBound = TimeDelta::Millis(200); - -std::string Config(bool enabled, bool valid) { - char buffer[1024]; - rtc::SimpleStringBuilder config_string(buffer); - - config_string << "WebRTC-Bwe-LossBasedBweV2/"; - - if (enabled) { - config_string << "Enabled:true"; - } else { - config_string << "Enabled:false"; +constexpr TimeDelta kDelayedIncreaseWindow = TimeDelta::Millis(300); +constexpr double kMaxIncreaseFactor = 1.5; + +class LossBasedBweV2Test : public ::testing::TestWithParam { + protected: + std::string Config(bool enabled, + bool valid, + bool trendline_integration_enabled) { + char buffer[1024]; + rtc::SimpleStringBuilder config_string(buffer); + + config_string << "WebRTC-Bwe-LossBasedBweV2/"; + + if (enabled) { + config_string << "Enabled:true"; + } else { + config_string << "Enabled:false"; + } + + if (valid) { + config_string << ",BwRampupUpperBoundFactor:1.2"; + } else { + config_string << ",BwRampupUpperBoundFactor:0.0"; + } + + if (trendline_integration_enabled) { + config_string << ",TrendlineIntegrationEnabled:true"; + } else { + config_string << ",TrendlineIntegrationEnabled:false"; + } + + config_string + << ",CandidateFactors:1.1|1.0|0.95,HigherBwBiasFactor:0.01," + "DelayBasedCandidate:true," + "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:" + "14kbps," + "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01," + "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15," + "SendingRateSmoothingFactor:0.01," + "InstantUpperBoundTemporalWeightFactor:0.97," + "InstantUpperBoundBwBalance:90kbps," + "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98"; + + config_string.AppendFormat( + ",ObservationDurationLowerBound:%dms", + static_cast(kObservationDurationLowerBound.ms())); + config_string.AppendFormat(",MaxIncreaseFactor:%f", kMaxIncreaseFactor); + config_string.AppendFormat(",DelayedIncreaseWindow:%dms", + static_cast(kDelayedIncreaseWindow.ms())); + + config_string << "/"; + + return config_string.str(); } - if (valid) { - config_string << ",BwRampupUpperBoundFactor:1.2"; - } else { - config_string << ",BwRampupUpperBoundFactor:0.0"; + std::vector CreatePacketResultsWithReceivedPackets( + Timestamp first_packet_timestamp) { + std::vector enough_feedback(2); + enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.send_time = first_packet_timestamp; + enough_feedback[1].sent_packet.send_time = + first_packet_timestamp + kObservationDurationLowerBound; + enough_feedback[0].receive_time = + first_packet_timestamp + kObservationDurationLowerBound; + enough_feedback[1].receive_time = + first_packet_timestamp + 2 * kObservationDurationLowerBound; + return enough_feedback; } - config_string - << ",CandidateFactors:0.9|1.1,HigherBwBiasFactor:0.01," - "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:14kbps," - "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01," - "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15," - "SendingRateSmoothingFactor:0.01," - "InstantUpperBoundTemporalWeightFactor:0.97," - "InstantUpperBoundBwBalance:90kbps," - "InstantUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98"; - - config_string.AppendFormat( - ",ObservationDurationLowerBound:%dms", - static_cast(kObservationDurationLowerBound.ms())); - - config_string << "/"; + std::vector CreatePacketResultsWith50pLossRate( + Timestamp first_packet_timestamp) { + std::vector enough_feedback(2); + enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.send_time = first_packet_timestamp; + enough_feedback[1].sent_packet.send_time = + first_packet_timestamp + kObservationDurationLowerBound; + enough_feedback[0].receive_time = + first_packet_timestamp + kObservationDurationLowerBound; + enough_feedback[1].receive_time = Timestamp::PlusInfinity(); + return enough_feedback; + } - return config_string.str(); -} + std::vector CreatePacketResultsWith100pLossRate( + Timestamp first_packet_timestamp) { + std::vector enough_feedback(2); + enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.send_time = first_packet_timestamp; + enough_feedback[1].sent_packet.send_time = + first_packet_timestamp + kObservationDurationLowerBound; + enough_feedback[0].receive_time = Timestamp::PlusInfinity(); + enough_feedback[1].receive_time = Timestamp::PlusInfinity(); + return enough_feedback; + } +}; -TEST(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) { +TEST_P(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) { ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); EXPECT_TRUE(loss_based_bandwidth_estimator.IsEnabled()); } -TEST(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) { +TEST_P(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) { ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/false, /*valid=*/true)); + Config(/*enabled=*/false, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); } -TEST(LossBasedBweV2Test, DisabledWhenGivenNonValidConfigurationValues) { +TEST_P(LossBasedBweV2Test, DisabledWhenGivenNonValidConfigurationValues) { ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/false)); + Config(/*enabled=*/true, /*valid=*/false, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); } -TEST(LossBasedBweV2Test, DisabledWhenGivenNonPositiveCandidateFactor) { +TEST_P(LossBasedBweV2Test, DisabledWhenGivenNonPositiveCandidateFactor) { ExplicitKeyValueConfig key_value_config_negative_candidate_factor( "WebRTC-Bwe-LossBasedBweV2/Enabled:true,CandidateFactors:-1.3|1.1/"); LossBasedBweV2 loss_based_bandwidth_estimator_1( @@ -104,8 +169,8 @@ TEST(LossBasedBweV2Test, DisabledWhenGivenNonPositiveCandidateFactor) { EXPECT_FALSE(loss_based_bandwidth_estimator_2.IsEnabled()); } -TEST(LossBasedBweV2Test, - DisabledWhenGivenConfigurationThatDoesNotAllowGeneratingCandidates) { +TEST_P(LossBasedBweV2Test, + DisabledWhenGivenConfigurationThatDoesNotAllowGeneratingCandidates) { ExplicitKeyValueConfig key_value_config( "WebRTC-Bwe-LossBasedBweV2/" "Enabled:true,CandidateFactors:1.0,AckedRateCandidate:false," @@ -114,56 +179,49 @@ TEST(LossBasedBweV2Test, EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); } -TEST(LossBasedBweV2Test, BandwidthEstimateGivenInitializationAndThenFeedback) { - PacketResult enough_feedback[2]; - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[0].sent_packet.send_time = Timestamp::Zero(); - enough_feedback[1].sent_packet.send_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback[0].receive_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback[1].receive_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; +TEST_P(LossBasedBweV2Test, + BandwidthEstimateGivenInitializationAndThenFeedback) { + std::vector enough_feedback = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_TRUE(loss_based_bandwidth_estimator.IsReady()); - EXPECT_TRUE(loss_based_bandwidth_estimator.GetBandwidthEstimate().IsFinite()); + EXPECT_TRUE( + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsFinite()); } -TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { - PacketResult enough_feedback[2]; - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[0].sent_packet.send_time = Timestamp::Zero(); - enough_feedback[1].sent_packet.send_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback[0].receive_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback[1].receive_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; - +TEST_P(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { + std::vector enough_feedback = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback, DataRate::PlusInfinity()); + enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } -TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { +TEST_P(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { // Create packet results where the observation duration is less than the lower // bound. PacketResult not_enough_feedback[2]; @@ -178,7 +236,8 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { Timestamp::Zero() + kObservationDurationLowerBound; ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( @@ -186,91 +245,72 @@ TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - not_enough_feedback, DataRate::PlusInfinity()); + not_enough_feedback, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); EXPECT_TRUE( - loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + loss_based_bandwidth_estimator + .GetBandwidthEstimate(/*delay_based_limit=*/DataRate::PlusInfinity()) + .IsPlusInfinity()); } -TEST(LossBasedBweV2Test, - SetValueIsTheEstimateUntilAdditionalFeedbackHasBeenReceived) { - PacketResult enough_feedback_1[2]; - PacketResult enough_feedback_2[2]; - enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); - enough_feedback_1[1].sent_packet.send_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback_2[0].sent_packet.send_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; - enough_feedback_2[1].sent_packet.send_time = - Timestamp::Zero() + 3 * kObservationDurationLowerBound; - enough_feedback_1[0].receive_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback_1[1].receive_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; - enough_feedback_2[0].receive_time = - Timestamp::Zero() + 3 * kObservationDurationLowerBound; - enough_feedback_2[1].receive_time = - Timestamp::Zero() + 4 * kObservationDurationLowerBound; +TEST_P(LossBasedBweV2Test, + SetValueIsTheEstimateUntilAdditionalFeedbackHasBeenReceived) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(600)); } -TEST(LossBasedBweV2Test, - SetAcknowledgedBitrateOnlyAffectsTheBweWhenAdditionalFeedbackIsGiven) { - PacketResult enough_feedback_1[2]; - PacketResult enough_feedback_2[2]; - enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); - enough_feedback_1[1].sent_packet.send_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback_2[0].sent_packet.send_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; - enough_feedback_2[1].sent_packet.send_time = - Timestamp::Zero() + 3 * kObservationDurationLowerBound; - enough_feedback_1[0].receive_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback_1[1].receive_time = - Timestamp::Zero() + 2 * kObservationDurationLowerBound; - enough_feedback_2[0].receive_time = - Timestamp::Zero() + 3 * kObservationDurationLowerBound; - enough_feedback_2[1].receive_time = - Timestamp::Zero() + 4 * kObservationDurationLowerBound; +TEST_P(LossBasedBweV2Test, + SetAcknowledgedBitrateOnlyAffectsTheBweWhenAdditionalFeedbackIsGiven) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator_1(&key_value_config); LossBasedBweV2 loss_based_bandwidth_estimator_2(&key_value_config); @@ -279,57 +319,452 @@ TEST(LossBasedBweV2Test, loss_based_bandwidth_estimator_2.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_1, DataRate::PlusInfinity()); + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.SetAcknowledgedBitrate( DataRate::KilobitsPerSec(600)); - EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(660)); loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate( - enough_feedback_2, DataRate::PlusInfinity()); + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); - EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), - loss_based_bandwidth_estimator_2.GetBandwidthEstimate()); + EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + loss_based_bandwidth_estimator_2.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity())); } -TEST(LossBasedBweV2Test, - BandwidthEstimateIsCappedToBeTcpFairGivenTooHighLossRate) { - PacketResult enough_feedback_no_received_packets[2]; - enough_feedback_no_received_packets[0].sent_packet.size = - DataSize::Bytes(15'000); - enough_feedback_no_received_packets[1].sent_packet.size = - DataSize::Bytes(15'000); - enough_feedback_no_received_packets[0].sent_packet.send_time = - Timestamp::Zero(); - enough_feedback_no_received_packets[1].sent_packet.send_time = - Timestamp::Zero() + kObservationDurationLowerBound; - enough_feedback_no_received_packets[0].receive_time = - Timestamp::PlusInfinity(); - enough_feedback_no_received_packets[1].receive_time = - Timestamp::PlusInfinity(); +TEST_P(LossBasedBweV2Test, + BandwidthEstimateIsCappedToBeTcpFairGivenTooHighLossRate) { + std::vector enough_feedback_no_received_packets = + CreatePacketResultsWith100pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero()); ExplicitKeyValueConfig key_value_config( - Config(/*enabled=*/true, /*valid=*/true)); + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(600)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - enough_feedback_no_received_packets, DataRate::PlusInfinity()); + enough_feedback_no_received_packets, DataRate::PlusInfinity(), + BandwidthUsage::kBwNormal); - EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), DataRate::KilobitsPerSec(100)); } -} // namespace +TEST_P(LossBasedBweV2Test, BandwidthEstimateNotIncreaseWhenNetworkUnderusing) { + if (!GetParam()) { + GTEST_SKIP() << "This test should run only if " + "trendline_integration_enabled is enabled"; + } + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); +} + +// When network is normal, estimate can increase but never be higher than +// the delay based estimate. +TEST_P(LossBasedBweV2Test, + BandwidthEstimateCappedByDelayBasedEstimateWhenNetworkNormal) { + // Create two packet results, network is in normal state, 100% packets are + // received, and no delay increase. + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + // If the delay based estimate is infinity, then loss based estimate increases + // and not bounded by delay based estimate. + EXPECT_GT(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + // If the delay based estimate is not infinity, then loss based estimate is + // bounded by delay based estimate. + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::KilobitsPerSec(500)), + DataRate::KilobitsPerSec(500)); +} + +// When loss based bwe receives a strong signal of overusing and an increase in +// loss rate, it should acked bitrate for emegency backoff. +TEST_P(LossBasedBweV2Test, UseAckedBitrateForEmegencyBackOff) { + // Create two packet results, first packet has 50% loss rate, second packet + // has 100% loss rate. + std::vector enough_feedback_1 = + CreatePacketResultsWith50pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWith100pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); + + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + DataRate acked_bitrate = DataRate::KilobitsPerSec(300); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate(acked_bitrate); + // Update estimate when network is overusing, and 50% loss rate. + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), + BandwidthUsage::kBwOverusing); + // Update estimate again when network is continuously overusing, and 100% + // loss rate. + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), + BandwidthUsage::kBwOverusing); + // The estimate bitrate now is backed off based on acked bitrate. + EXPECT_LE(loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()), + acked_bitrate); +} + +// When receiving the same packet feedback, loss based bwe ignores the feedback +// and returns the current estimate. +TEST_P(LossBasedBweV2Test, NoBweChangeIfObservationDurationUnchanged) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_1 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + + // Use the same feedback and check if the estimate is unchanged. + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_2 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + EXPECT_EQ(estimate_2, estimate_1); +} + +// When receiving feedback of packets that were sent within an observation +// duration, and network is in the normal state, loss based bwe returns the +// current estimate. +TEST_P(LossBasedBweV2Test, + NoBweChangeIfObservationDurationIsSmallAndNetworkNormal) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kObservationDurationLowerBound - TimeDelta::Millis(1)); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_1 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_2 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + EXPECT_EQ(estimate_2, estimate_1); +} + +// When receiving feedback of packets that were sent within an observation +// duration, and network is in the underusing state, loss based bwe returns the +// current estimate. +TEST_P(LossBasedBweV2Test, + NoBweIncreaseIfObservationDurationIsSmallAndNetworkUnderusing) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kObservationDurationLowerBound - TimeDelta::Millis(1)); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_1 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), + BandwidthUsage::kBwUnderusing); + DataRate estimate_2 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + EXPECT_LE(estimate_2, estimate_1); +} + +// When receiving feedback of packets that were sent within an observation +// duration, network is overusing, and trendline integration is enabled, loss +// based bwe updates its estimate. +TEST_P(LossBasedBweV2Test, + UpdateEstimateIfObservationDurationIsSmallAndNetworkOverusing) { + if (!GetParam()) { + GTEST_SKIP() << "This test should run only if " + "trendline_integration_enabled is enabled"; + } + std::vector enough_feedback_1 = + CreatePacketResultsWith50pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWith100pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero() + + kObservationDurationLowerBound - TimeDelta::Millis(1)); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, DataRate::PlusInfinity(), BandwidthUsage::kBwNormal); + DataRate estimate_1 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, DataRate::PlusInfinity(), + BandwidthUsage::kBwOverusing); + DataRate estimate_2 = loss_based_bandwidth_estimator.GetBandwidthEstimate( + /*delay_based_limit=*/DataRate::PlusInfinity()); + EXPECT_LT(estimate_2, estimate_1); +} + +TEST_P(LossBasedBweV2Test, + IncreaseToDelayBasedEstimateIfNoLossOrDelayIncrease) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + 2 * kObservationDurationLowerBound); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal); + EXPECT_EQ( + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate), + delay_based_estimate); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal); + EXPECT_EQ( + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate), + delay_based_estimate); +} + +// After loss based bwe backs off, the next estimate is capped by +// MaxIncreaseFactor * current estimate. +TEST_P(LossBasedBweV2Test, + IncreaseByMaxIncreaseFactorAfterLossBasedBweBacksOff) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kObservationDurationLowerBound); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal); + DataRate estimate_1 = + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate); + // Increase the acknowledged bitrate to make sure that the estimate is not + // capped too low. + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(5000)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal); + + // The estimate is capped by current_estimate * kMaxIncreaseFactor because it + // recently backed off. + DataRate estimate_2 = + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate); + EXPECT_EQ(estimate_2, estimate_1 * kMaxIncreaseFactor); + EXPECT_LE(estimate_2, delay_based_estimate); +} + +// After loss based bwe backs off, the estimate is bounded during the delayed +// window. +TEST_P(LossBasedBweV2Test, + EstimateBitrateIsBoundedDuringDelayedWindowAfterLossBasedBweBacksOff) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWith50pLossRate( + /*first_packet_timestamp=*/Timestamp::Zero() + + kDelayedIncreaseWindow - TimeDelta::Millis(2)); + std::vector enough_feedback_3 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kDelayedIncreaseWindow - TimeDelta::Millis(1)); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal); + // Increase the acknowledged bitrate to make sure that the estimate is not + // capped too low. + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(5000)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal); + + // The estimate is capped by current_estimate * kMaxIncreaseFactor because + // it recently backed off. + DataRate estimate_2 = + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_3, delay_based_estimate, BandwidthUsage::kBwNormal); + // The latest estimate is the same as the previous estimate since the sent + // packets were sent within the DelayedIncreaseWindow. + EXPECT_EQ( + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate), + estimate_2); +} + +// The estimate is not bounded after the delayed increase window. +TEST_P(LossBasedBweV2Test, KeepIncreasingEstimateAfterDelayedIncreaseWindow) { + std::vector enough_feedback_1 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero()); + std::vector enough_feedback_2 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kDelayedIncreaseWindow - TimeDelta::Millis(1)); + std::vector enough_feedback_3 = + CreatePacketResultsWithReceivedPackets( + /*first_packet_timestamp=*/Timestamp::Zero() + + kDelayedIncreaseWindow + TimeDelta::Millis(1)); + ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true, + /*trendline_integration_enabled=*/GetParam())); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + DataRate delay_based_estimate = DataRate::KilobitsPerSec(5000); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(300)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_1, delay_based_estimate, BandwidthUsage::kBwNormal); + // Increase the acknowledged bitrate to make sure that the estimate is not + // capped too low. + loss_based_bandwidth_estimator.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(5000)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_2, delay_based_estimate, BandwidthUsage::kBwNormal); + + // The estimate is capped by current_estimate * kMaxIncreaseFactor because it + // recently backed off. + DataRate estimate_2 = + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_3, delay_based_estimate, BandwidthUsage::kBwNormal); + // The estimate can continue increasing after the DelayedIncreaseWindow. + EXPECT_GE( + loss_based_bandwidth_estimator.GetBandwidthEstimate(delay_based_estimate), + estimate_2); +} + +INSTANTIATE_TEST_SUITE_P(LossBasedBweV2Tests, + LossBasedBweV2Test, + ::testing::Bool()); + +} // namespace } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/probe_controller.cc b/modules/congestion_controller/goog_cc/probe_controller.cc index df753ed0c9..9c263ebe52 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.cc +++ b/modules/congestion_controller/goog_cc/probe_controller.cc @@ -91,7 +91,7 @@ void MaybeLogProbeClusterCreated(RtcEventLog* event_log, } // namespace ProbeControllerConfig::ProbeControllerConfig( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : first_exponential_probe_scale("p1", 3.0), second_exponential_probe_scale("p2", 6.0), further_exponential_probe_scale("step_size", 2), @@ -127,7 +127,7 @@ ProbeControllerConfig::ProbeControllerConfig(const ProbeControllerConfig&) = default; ProbeControllerConfig::~ProbeControllerConfig() = default; -ProbeController::ProbeController(const WebRtcKeyValueConfig* key_value_config, +ProbeController::ProbeController(const FieldTrialsView* key_value_config, RtcEventLog* event_log) : enable_periodic_alr_probing_(false), in_rapid_recovery_experiment_(absl::StartsWith( diff --git a/modules/congestion_controller/goog_cc/probe_controller.h b/modules/congestion_controller/goog_cc/probe_controller.h index d0f1458ece..86931ee8b1 100644 --- a/modules/congestion_controller/goog_cc/probe_controller.h +++ b/modules/congestion_controller/goog_cc/probe_controller.h @@ -18,16 +18,16 @@ #include "absl/base/attributes.h" #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/network_control.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { struct ProbeControllerConfig { - explicit ProbeControllerConfig(const WebRtcKeyValueConfig* key_value_config); + explicit ProbeControllerConfig(const FieldTrialsView* key_value_config); ProbeControllerConfig(const ProbeControllerConfig&); ProbeControllerConfig& operator=(const ProbeControllerConfig&) = default; ~ProbeControllerConfig(); @@ -58,7 +58,7 @@ struct ProbeControllerConfig { // bitrate is adjusted by an application. class ProbeController { public: - explicit ProbeController(const WebRtcKeyValueConfig* key_value_config, + explicit ProbeController(const FieldTrialsView* key_value_config, RtcEventLog* event_log); ~ProbeController(); diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc b/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc index 1169e9f6bb..93909afab6 100644 --- a/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator.cc @@ -15,24 +15,55 @@ #include #include +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/checks.h" namespace webrtc { RobustThroughputEstimator::RobustThroughputEstimator( const RobustThroughputEstimatorSettings& settings) - : settings_(settings) { + : settings_(settings), + latest_discarded_send_time_(Timestamp::MinusInfinity()) { RTC_DCHECK(settings.enabled); } RobustThroughputEstimator::~RobustThroughputEstimator() {} +bool RobustThroughputEstimator::FirstPacketOutsideWindow() { + if (window_.empty()) + return false; + if (window_.size() > settings_.max_window_packets) + return true; + TimeDelta current_window_duration = + window_.back().receive_time - window_.front().receive_time; + if (current_window_duration > settings_.max_window_duration) + return true; + if (window_.size() > settings_.window_packets && + current_window_duration > settings_.min_window_duration) { + return true; + } + return false; +} + void RobustThroughputEstimator::IncomingPacketFeedbackVector( const std::vector& packet_feedback_vector) { RTC_DCHECK(std::is_sorted(packet_feedback_vector.begin(), packet_feedback_vector.end(), PacketResult::ReceiveTimeOrder())); for (const auto& packet : packet_feedback_vector) { + // Ignore packets without valid send or receive times. + // (This should not happen in production since lost packets are filtered + // out before passing the feedback vector to the throughput estimator. + // However, explicitly handling this case makes the estimator more robust + // and avoids a hard-to-detect bad state.) + if (packet.receive_time.IsInfinite() || + packet.sent_packet.send_time.IsInfinite()) { + continue; + } + // Insert the new packet. window_.push_back(packet); window_.back().sent_packet.prior_unacked_data = @@ -45,24 +76,24 @@ void RobustThroughputEstimator::IncomingPacketFeedbackVector( i > 0 && window_[i].receive_time < window_[i - 1].receive_time; i--) { std::swap(window_[i], window_[i - 1]); } - // Remove old packets. - while (window_.size() > settings_.kMaxPackets || - (window_.size() > settings_.min_packets && - packet.receive_time - window_.front().receive_time > - settings_.window_duration)) { - window_.pop_front(); - } + } + + // Remove old packets. + while (FirstPacketOutsideWindow()) { + latest_discarded_send_time_ = std::max( + latest_discarded_send_time_, window_.front().sent_packet.send_time); + window_.pop_front(); } } absl::optional RobustThroughputEstimator::bitrate() const { - if (window_.size() < settings_.initial_packets) + if (window_.empty() || window_.size() < settings_.required_packets) return absl::nullopt; TimeDelta largest_recv_gap(TimeDelta::Millis(0)); TimeDelta second_largest_recv_gap(TimeDelta::Millis(0)); for (size_t i = 1; i < window_.size(); i++) { - // Find receive time gaps + // Find receive time gaps. TimeDelta gap = window_[i].receive_time - window_[i - 1].receive_time; if (gap > largest_recv_gap) { second_largest_recv_gap = largest_recv_gap; @@ -72,63 +103,86 @@ absl::optional RobustThroughputEstimator::bitrate() const { } } - Timestamp min_send_time = window_[0].sent_packet.send_time; - Timestamp max_send_time = window_[0].sent_packet.send_time; - Timestamp min_recv_time = window_[0].receive_time; - Timestamp max_recv_time = window_[0].receive_time; - DataSize data_size = DataSize::Bytes(0); + Timestamp first_send_time = Timestamp::PlusInfinity(); + Timestamp last_send_time = Timestamp::MinusInfinity(); + Timestamp first_recv_time = Timestamp::PlusInfinity(); + Timestamp last_recv_time = Timestamp::MinusInfinity(); + DataSize recv_size = DataSize::Bytes(0); + DataSize send_size = DataSize::Bytes(0); + DataSize first_recv_size = DataSize::Bytes(0); + DataSize last_send_size = DataSize::Bytes(0); + size_t num_sent_packets_in_window = 0; for (const auto& packet : window_) { - min_send_time = std::min(min_send_time, packet.sent_packet.send_time); - max_send_time = std::max(max_send_time, packet.sent_packet.send_time); - min_recv_time = std::min(min_recv_time, packet.receive_time); - max_recv_time = std::max(max_recv_time, packet.receive_time); - data_size += packet.sent_packet.size; - data_size += packet.sent_packet.prior_unacked_data; + if (packet.receive_time < first_recv_time) { + first_recv_time = packet.receive_time; + first_recv_size = + packet.sent_packet.size + packet.sent_packet.prior_unacked_data; + } + last_recv_time = std::max(last_recv_time, packet.receive_time); + recv_size += packet.sent_packet.size; + recv_size += packet.sent_packet.prior_unacked_data; + + if (packet.sent_packet.send_time < latest_discarded_send_time_) { + // If we have dropped packets from the window that were sent after + // this packet, then this packet was reordered. Ignore it from + // the send rate computation (since the send time may be very far + // in the past, leading to underestimation of the send rate.) + // However, ignoring packets creates a risk that we end up without + // any packets left to compute a send rate. + continue; + } + if (packet.sent_packet.send_time > last_send_time) { + last_send_time = packet.sent_packet.send_time; + last_send_size = + packet.sent_packet.size + packet.sent_packet.prior_unacked_data; + } + first_send_time = std::min(first_send_time, packet.sent_packet.send_time); + + send_size += packet.sent_packet.size; + send_size += packet.sent_packet.prior_unacked_data; + ++num_sent_packets_in_window; } // Suppose a packet of size S is sent every T milliseconds. // A window of N packets would contain N*S bytes, but the time difference // between the first and the last packet would only be (N-1)*T. Thus, we - // need to remove one packet. - DataSize recv_size = data_size; - DataSize send_size = data_size; - if (settings_.assume_shared_link) { - // Depending on how the bottleneck queue is implemented, a large packet - // may delay sending of sebsequent packets, so the delay between packets - // i and i+1 depends on the size of both packets. In this case we minimize - // the maximum error by removing half of both the first and last packet - // size. - DataSize first_last_average_size = - (window_.front().sent_packet.size + - window_.front().sent_packet.prior_unacked_data + - window_.back().sent_packet.size + - window_.back().sent_packet.prior_unacked_data) / - 2; - recv_size -= first_last_average_size; - send_size -= first_last_average_size; - } else { - // In the simpler case where the delay between packets i and i+1 only - // depends on the size of packet i+1, the first packet doesn't give us - // any information. Analogously, we assume that the start send time - // for the last packet doesn't depend on the size of the packet. - recv_size -= (window_.front().sent_packet.size + - window_.front().sent_packet.prior_unacked_data); - send_size -= (window_.back().sent_packet.size + - window_.back().sent_packet.prior_unacked_data); - } + // need to remove the size of one packet to get the correct rate of S/T. + // Which packet to remove (if the packets have varying sizes), + // depends on the network model. + // Suppose that 2 packets with sizes s1 and s2, are received at times t1 + // and t2, respectively. If the packets were transmitted back to back over + // a bottleneck with rate capacity r, then we'd expect t2 = t1 + r * s2. + // Thus, r = (t2-t1) / s2, so the size of the first packet doesn't affect + // the difference between t1 and t2. + // Analoguously, if the first packet is sent at time t1 and the sender + // paces the packets at rate r, then the second packet can be sent at time + // t2 = t1 + r * s1. Thus, the send rate estimate r = (t2-t1) / s1 doesn't + // depend on the size of the last packet. + recv_size -= first_recv_size; + send_size -= last_send_size; - // Remove the largest gap by replacing it by the second largest gap - // or the average gap. - TimeDelta send_duration = max_send_time - min_send_time; - TimeDelta recv_duration = (max_recv_time - min_recv_time) - largest_recv_gap; - if (settings_.reduce_bias) { - recv_duration += second_largest_recv_gap; - } else { - recv_duration += recv_duration / (window_.size() - 2); + // Remove the largest gap by replacing it by the second largest gap. + // This is to ensure that spurious "delay spikes" (i.e. when the + // network stops transmitting packets for a short period, followed + // by a burst of delayed packets), don't cause the estimate to drop. + // This could cause an overestimation, which we guard against by + // never returning an estimate above the send rate. + RTC_DCHECK(first_recv_time.IsFinite()); + RTC_DCHECK(last_recv_time.IsFinite()); + TimeDelta recv_duration = (last_recv_time - first_recv_time) - + largest_recv_gap + second_largest_recv_gap; + recv_duration = std::max(recv_duration, TimeDelta::Millis(1)); + + if (num_sent_packets_in_window < settings_.required_packets) { + // Too few send times to calculate a reliable send rate. + return recv_size / recv_duration; } + RTC_DCHECK(first_send_time.IsFinite()); + RTC_DCHECK(last_send_time.IsFinite()); + TimeDelta send_duration = last_send_time - first_send_time; send_duration = std::max(send_duration, TimeDelta::Millis(1)); - recv_duration = std::max(recv_duration, TimeDelta::Millis(1)); + return std::min(send_size / send_duration, recv_size / recv_duration); } diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator.h b/modules/congestion_controller/goog_cc/robust_throughput_estimator.h index de48a9b599..9d89856496 100644 --- a/modules/congestion_controller/goog_cc/robust_throughput_estimator.h +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator.h @@ -12,13 +12,12 @@ #define MODULES_CONGESTION_CONTROLLER_GOOG_CC_ROBUST_THROUGHPUT_ESTIMATOR_H_ #include -#include #include #include "absl/types/optional.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" +#include "api/units/timestamp.h" #include "modules/congestion_controller/goog_cc/acknowledged_bitrate_estimator_interface.h" namespace webrtc { @@ -39,8 +38,11 @@ class RobustThroughputEstimator : public AcknowledgedBitrateEstimatorInterface { void SetAlrEndedTime(Timestamp /*alr_ended_time*/) override {} private: + bool FirstPacketOutsideWindow(); + const RobustThroughputEstimatorSettings settings_; std::deque window_; + Timestamp latest_discarded_send_time_ = Timestamp::MinusInfinity(); }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc b/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc index d2e01d362c..95ac525640 100644 --- a/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc +++ b/modules/congestion_controller/goog_cc/robust_throughput_estimator_unittest.cc @@ -10,158 +10,418 @@ #include "modules/congestion_controller/goog_cc/robust_throughput_estimator.h" -#include "api/transport/field_trial_based_config.h" -#include "test/field_trial.h" +#include +#include + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "test/explicit_key_value_config.h" #include "test/gtest.h" namespace webrtc { -namespace { -std::vector CreateFeedbackVector(size_t number_of_packets, - DataSize packet_size, - TimeDelta send_increment, - TimeDelta recv_increment, - Timestamp* send_clock, - Timestamp* recv_clock, - uint16_t* sequence_number) { - std::vector packet_feedback_vector(number_of_packets); - for (size_t i = 0; i < number_of_packets; i++) { - packet_feedback_vector[i].receive_time = *recv_clock; - packet_feedback_vector[i].sent_packet.send_time = *send_clock; - packet_feedback_vector[i].sent_packet.sequence_number = *sequence_number; - packet_feedback_vector[i].sent_packet.size = packet_size; - *send_clock += send_increment; - *recv_clock += recv_increment; - *sequence_number += 1; - } - return packet_feedback_vector; + +RobustThroughputEstimatorSettings CreateRobustThroughputEstimatorSettings( + absl::string_view field_trial_string) { + test::ExplicitKeyValueConfig trials(field_trial_string); + RobustThroughputEstimatorSettings settings(&trials); + return settings; } -} // anonymous namespace - -TEST(RobustThroughputEstimatorTest, SteadyRate) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/" - "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," - "window_duration:100ms/"); - FieldTrialBasedConfig field_trial_config; - RobustThroughputEstimatorSettings settings(&field_trial_config); - RobustThroughputEstimator throughput_estimator(settings); - DataSize packet_size(DataSize::Bytes(1000)); - Timestamp send_clock(Timestamp::Millis(100000)); - Timestamp recv_clock(Timestamp::Millis(10000)); - TimeDelta send_increment(TimeDelta::Millis(10)); - TimeDelta recv_increment(TimeDelta::Millis(10)); - uint16_t sequence_number = 100; + +class FeedbackGenerator { + public: + std::vector CreateFeedbackVector(size_t number_of_packets, + DataSize packet_size, + DataRate send_rate, + DataRate recv_rate) { + std::vector packet_feedback_vector(number_of_packets); + for (size_t i = 0; i < number_of_packets; i++) { + packet_feedback_vector[i].sent_packet.send_time = send_clock_; + packet_feedback_vector[i].sent_packet.sequence_number = sequence_number_; + packet_feedback_vector[i].sent_packet.size = packet_size; + send_clock_ += packet_size / send_rate; + recv_clock_ += packet_size / recv_rate; + sequence_number_ += 1; + packet_feedback_vector[i].receive_time = recv_clock_; + } + return packet_feedback_vector; + } + + Timestamp CurrentReceiveClock() { return recv_clock_; } + + void AdvanceReceiveClock(TimeDelta delta) { recv_clock_ += delta; } + + void AdvanceSendClock(TimeDelta delta) { send_clock_ += delta; } + + private: + Timestamp send_clock_ = Timestamp::Millis(100000); + Timestamp recv_clock_ = Timestamp::Millis(10000); + uint16_t sequence_number_ = 100; +}; + +TEST(RobustThroughputEstimatorTest, InitialEstimate) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + + // No estimate until the estimator has enough data. std::vector packet_feedback = - CreateFeedbackVector(9, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + feedback_generator.CreateFeedbackVector(9, DataSize::Bytes(1000), + send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); EXPECT_FALSE(throughput_estimator.bitrate().has_value()); - packet_feedback = - CreateFeedbackVector(11, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + // Estimate once `required_packets` packets have been received. + packet_feedback = feedback_generator.CreateFeedbackVector( + 1, DataSize::Bytes(1000), send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); auto throughput = throughput_estimator.bitrate(); - EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, - 0.05 * 100 * 1000.0); // Allow 5% error + EXPECT_EQ(throughput, send_rate); + + // Estimate remains stable when send and receive rates are stable. + packet_feedback = feedback_generator.CreateFeedbackVector( + 15, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); } -TEST(RobustThroughputEstimatorTest, DelaySpike) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/" - "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," - "window_duration:100ms/"); - FieldTrialBasedConfig field_trial_config; - RobustThroughputEstimatorSettings settings(&field_trial_config); - RobustThroughputEstimator throughput_estimator(settings); - DataSize packet_size(DataSize::Bytes(1000)); - Timestamp send_clock(Timestamp::Millis(100000)); - Timestamp recv_clock(Timestamp::Millis(10000)); - TimeDelta send_increment(TimeDelta::Millis(10)); - TimeDelta recv_increment(TimeDelta::Millis(10)); - uint16_t sequence_number = 100; +TEST(RobustThroughputEstimatorTest, EstimateAdapts) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + + // 1 second, 800kbps, estimate is stable. + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + for (int i = 0; i < 10; ++i) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } + + // 1 second, 1600kbps, estimate increases + send_rate = DataRate::BytesPerSec(200000); + recv_rate = DataRate::BytesPerSec(200000); + for (int i = 0; i < 20; ++i) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_GE(throughput.value(), DataRate::BytesPerSec(100000)); + EXPECT_LE(throughput.value(), send_rate); + } + + // 1 second, 1600kbps, estimate is stable + for (int i = 0; i < 20; ++i) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } + + // 1 second, 400kbps, estimate decreases + send_rate = DataRate::BytesPerSec(50000); + recv_rate = DataRate::BytesPerSec(50000); + for (int i = 0; i < 5; ++i) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_LE(throughput.value(), DataRate::BytesPerSec(200000)); + EXPECT_GE(throughput.value(), send_rate); + } + + // 1 second, 400kbps, estimate is stable + send_rate = DataRate::BytesPerSec(50000); + recv_rate = DataRate::BytesPerSec(50000); + for (int i = 0; i < 5; ++i) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } +} + +TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(25000)); + std::vector packet_feedback = - CreateFeedbackVector(20, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); auto throughput = throughput_estimator.bitrate(); - EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, - 0.05 * 100 * 1000.0); // Allow 5% error + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + recv_rate.bytes_per_sec(), + 0.05 * recv_rate.bytes_per_sec()); // Allow 5% error +} - // Delay spike - recv_clock += TimeDelta::Millis(40); +TEST(RobustThroughputEstimatorTest, CappedBySendRate) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(50000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); - // Faster delivery after the gap - recv_increment = TimeDelta::Millis(2); - packet_feedback = - CreateFeedbackVector(5, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); - throughput = throughput_estimator.bitrate(); - EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, - 0.05 * 100 * 1000.0); // Allow 5% error - - // Delivery at normal rate. This will be capped by the send rate. - recv_increment = TimeDelta::Millis(10); - packet_feedback = - CreateFeedbackVector(5, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + auto throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec(), + 0.05 * send_rate.bytes_per_sec()); // Allow 5% error +} + +TEST(RobustThroughputEstimatorTest, DelaySpike) { + FeedbackGenerator feedback_generator; + // This test uses a 500ms window to amplify the effect + // of a delay spike. + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,window_duration:500ms/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); - throughput = throughput_estimator.bitrate(); - EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 100 * 1000.0, - 0.05 * 100 * 1000.0); // Allow 5% error + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + + // Delay spike. 25 packets sent, but none received. + feedback_generator.AdvanceReceiveClock(TimeDelta::Millis(250)); + + // Deliver all of the packets during the next 50 ms. (During this time, + // we'll have sent an additional 5 packets, so we need to receive 30 + // packets at 1000 bytes each in 50 ms, i.e. 600000 bytes per second). + recv_rate = DataRate::BytesPerSec(600000); + // Estimate should not drop. + for (int i = 0; i < 30; ++i) { + packet_feedback = feedback_generator.CreateFeedbackVector( + 1, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec(), + 0.05 * send_rate.bytes_per_sec()); // Allow 5% error + } + + // Delivery at normal rate. When the packets received before the gap + // has left the estimator's window, the receive rate will be high, but the + // estimate should be capped by the send rate. + recv_rate = DataRate::BytesPerSec(100000); + for (int i = 0; i < 20; ++i) { + packet_feedback = feedback_generator.CreateFeedbackVector( + 5, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec(), + 0.05 * send_rate.bytes_per_sec()); // Allow 5% error + } } -TEST(RobustThroughputEstimatorTest, CappedByReceiveRate) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/" - "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," - "window_duration:100ms/"); - FieldTrialBasedConfig field_trial_config; - RobustThroughputEstimatorSettings settings(&field_trial_config); - RobustThroughputEstimator throughput_estimator(settings); - DataSize packet_size(DataSize::Bytes(1000)); - Timestamp send_clock(Timestamp::Millis(100000)); - Timestamp recv_clock(Timestamp::Millis(10000)); - TimeDelta send_increment(TimeDelta::Millis(10)); - TimeDelta recv_increment(TimeDelta::Millis(40)); - uint16_t sequence_number = 100; +TEST(RobustThroughputEstimatorTest, HighLoss) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + std::vector packet_feedback = - CreateFeedbackVector(20, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); + + // 50% loss + for (size_t i = 0; i < packet_feedback.size(); i++) { + if (i % 2 == 1) { + packet_feedback[i].receive_time = Timestamp::PlusInfinity(); + } + } + + std::sort(packet_feedback.begin(), packet_feedback.end(), + PacketResult::ReceiveTimeOrder()); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); auto throughput = throughput_estimator.bitrate(); - EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 25 * 1000.0, - 0.05 * 25 * 1000.0); // Allow 5% error + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec() / 2, + 0.05 * send_rate.bytes_per_sec() / 2); // Allow 5% error } -TEST(RobustThroughputEstimatorTest, CappedBySendRate) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-Bwe-RobustThroughputEstimatorSettings/" - "enabled:true,assume_shared_link:false,reduce_bias:true,min_packets:10," - "window_duration:100ms/"); - FieldTrialBasedConfig field_trial_config; - RobustThroughputEstimatorSettings settings(&field_trial_config); - RobustThroughputEstimator throughput_estimator(settings); - DataSize packet_size(DataSize::Bytes(1000)); - Timestamp send_clock(Timestamp::Millis(100000)); - Timestamp recv_clock(Timestamp::Millis(10000)); - TimeDelta send_increment(TimeDelta::Millis(20)); - TimeDelta recv_increment(TimeDelta::Millis(10)); - uint16_t sequence_number = 100; +TEST(RobustThroughputEstimatorTest, ReorderedFeedback) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + + std::vector delayed_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + packet_feedback = feedback_generator.CreateFeedbackVector( + 10, DataSize::Bytes(1000), send_rate, recv_rate); + + // Since we're missing some feedback, it's expected that the + // estimate will drop. + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_LT(throughput.value(), send_rate); + + // But it should completely recover as soon as we get the feedback. + throughput_estimator.IncomingPacketFeedbackVector(delayed_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + + // It should then remain stable (as if the feedbacks weren't reordered.) + for (int i = 0; i < 10; ++i) { + packet_feedback = feedback_generator.CreateFeedbackVector( + 15, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } +} + +TEST(RobustThroughputEstimatorTest, DeepReordering) { + FeedbackGenerator feedback_generator; + // This test uses a 500ms window to amplify the + // effect of reordering. + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true,window_duration:500ms/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + + std::vector delayed_packets = + feedback_generator.CreateFeedbackVector(1, DataSize::Bytes(1000), + send_rate, recv_rate); + + for (int i = 0; i < 10; i++) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } + + // Delayed packet arrives ~1 second after it should have. + // Since the window is 500 ms, the delayed packet was sent ~500 + // ms before the second oldest packet. However, the send rate + // should not drop. + delayed_packets.front().receive_time = + feedback_generator.CurrentReceiveClock(); + throughput_estimator.IncomingPacketFeedbackVector(delayed_packets); + auto throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec(), + 0.05 * send_rate.bytes_per_sec()); // Allow 5% error + + // Thoughput should stay stable. + for (int i = 0; i < 10; i++) { + std::vector packet_feedback = + feedback_generator.CreateFeedbackVector(10, DataSize::Bytes(1000), + send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + auto throughput = throughput_estimator.bitrate(); + ASSERT_TRUE(throughput.has_value()); + EXPECT_NEAR(throughput.value().bytes_per_sec(), + send_rate.bytes_per_sec(), + 0.05 * send_rate.bytes_per_sec()); // Allow 5% error + } +} + +TEST(RobustThroughputEstimatorTest, StreamPausedAndResumed) { + FeedbackGenerator feedback_generator; + RobustThroughputEstimator throughput_estimator( + CreateRobustThroughputEstimatorSettings( + "WebRTC-Bwe-RobustThroughputEstimatorSettings/" + "enabled:true/")); + DataRate send_rate(DataRate::BytesPerSec(100000)); + DataRate recv_rate(DataRate::BytesPerSec(100000)); + std::vector packet_feedback = - CreateFeedbackVector(20, packet_size, send_increment, recv_increment, - &send_clock, &recv_clock, &sequence_number); + feedback_generator.CreateFeedbackVector(20, DataSize::Bytes(1000), + send_rate, recv_rate); throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); auto throughput = throughput_estimator.bitrate(); EXPECT_TRUE(throughput.has_value()); - EXPECT_NEAR(throughput.value().bytes_per_sec(), 50 * 1000.0, - 0.05 * 50 * 1000.0); // Allow 5% error + double expected_bytes_per_sec = 100 * 1000.0; + EXPECT_NEAR(throughput.value().bytes_per_sec(), + expected_bytes_per_sec, + 0.05 * expected_bytes_per_sec); // Allow 5% error + + // No packets sent or feedback received for 60s. + feedback_generator.AdvanceSendClock(TimeDelta::Seconds(60)); + feedback_generator.AdvanceReceiveClock(TimeDelta::Seconds(60)); + + // Resume sending packets at the same rate as before. The estimate + // will initially be invalid, due to lack of recent data. + packet_feedback = feedback_generator.CreateFeedbackVector( + 5, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_FALSE(throughput.has_value()); + + // But be back to the normal level once we have enough data. + for (int i = 0; i < 4; ++i) { + packet_feedback = feedback_generator.CreateFeedbackVector( + 5, DataSize::Bytes(1000), send_rate, recv_rate); + throughput_estimator.IncomingPacketFeedbackVector(packet_feedback); + throughput = throughput_estimator.bitrate(); + EXPECT_EQ(throughput, send_rate); + } } -} // namespace webrtc*/ +} // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc index 5bb145cf20..d0d7b83ae6 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc @@ -17,9 +17,10 @@ #include #include "absl/strings/match.h" +#include "api/field_trials_view.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "api/rtc_event_log/rtc_event_log.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" @@ -157,7 +158,7 @@ DataRate LinkCapacityTracker::estimate() const { return DataRate::BitsPerSec(capacity_estimate_bps_); } -RttBasedBackoff::RttBasedBackoff(const WebRtcKeyValueConfig* key_value_config) +RttBasedBackoff::RttBasedBackoff(const FieldTrialsView* key_value_config) : disabled_("Disabled"), configured_limit_("limit", TimeDelta::Seconds(3)), drop_fraction_("fraction", 0.8), @@ -196,7 +197,7 @@ TimeDelta RttBasedBackoff::CorrectedRtt(Timestamp at_time) const { RttBasedBackoff::~RttBasedBackoff() = default; SendSideBandwidthEstimation::SendSideBandwidthEstimation( - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, RtcEventLog* event_log) : rtt_backoff_(key_value_config), lost_packets_since_last_loss_update_(0), @@ -229,7 +230,8 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation( bitrate_threshold_(kDefaultBitrateThreshold), loss_based_bandwidth_estimator_v1_(key_value_config), loss_based_bandwidth_estimator_v2_(key_value_config), - disable_receiver_limit_caps_only_("Disabled") { + disable_receiver_limit_caps_only_("Disabled"), + delay_detector_state_(BandwidthUsage::kBwNormal) { RTC_DCHECK(event_log); if (BweLossExperimentIsEnabled()) { uint32_t bitrate_threshold_kbps; @@ -271,6 +273,7 @@ void SendSideBandwidthEstimation::OnRouteChange() { uma_update_state_ = kNoUpdate; uma_rtt_state_ = kNoUpdate; last_rtc_event_log_ = Timestamp::MinusInfinity(); + delay_detector_state_ = BandwidthUsage::kBwNormal; } void SendSideBandwidthEstimation::SetBitrates( @@ -330,9 +333,12 @@ void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, ApplyTargetLimits(at_time); } -void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time, - DataRate bitrate) { +void SendSideBandwidthEstimation::UpdateDelayBasedEstimate( + Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state) { link_capacity_.UpdateDelayBasedEstimate(at_time, bitrate); + delay_detector_state_ = delay_detector_state; // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no // limitation. delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate; @@ -356,7 +362,7 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( } } -void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( +void SendSideBandwidthEstimation::UpdateLossBasedEstimatorFromFeedbackVector( const TransportPacketsFeedback& report) { if (LossBasedBandwidthEstimatorV1Enabled()) { loss_based_bandwidth_estimator_v1_.UpdateLossStatistics( @@ -364,7 +370,7 @@ void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( } if (LossBasedBandwidthEstimatorV2Enabled()) { loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate( - report.packet_feedbacks, delay_based_limit_); + report.packet_feedbacks, delay_based_limit_, delay_detector_state_); } } @@ -509,8 +515,8 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { if (LossBasedBandwidthEstimatorV2ReadyForUse()) { DataRate new_bitrate = - loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate(); - new_bitrate = std::min(new_bitrate, delay_based_limit_); + loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate( + delay_based_limit_); UpdateTargetBitrate(new_bitrate, at_time); return; } diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h index f31f30f70d..cbcad86df4 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h @@ -20,8 +20,9 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" +#include "api/network_state_predictor.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" @@ -56,7 +57,7 @@ class LinkCapacityTracker { class RttBasedBackoff { public: - explicit RttBasedBackoff(const WebRtcKeyValueConfig* key_value_config); + explicit RttBasedBackoff(const FieldTrialsView* key_value_config); ~RttBasedBackoff(); void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt); TimeDelta CorrectedRtt(Timestamp at_time) const; @@ -77,7 +78,7 @@ class RttBasedBackoff { class SendSideBandwidthEstimation { public: SendSideBandwidthEstimation() = delete; - SendSideBandwidthEstimation(const WebRtcKeyValueConfig* key_value_config, + SendSideBandwidthEstimation(const FieldTrialsView* key_value_config, RtcEventLog* event_log); ~SendSideBandwidthEstimation(); @@ -97,7 +98,9 @@ class SendSideBandwidthEstimation { void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth); // Call when a new delay-based estimate is available. - void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate); + void UpdateDelayBasedEstimate(Timestamp at_time, + DataRate bitrate, + BandwidthUsage delay_detector_state); // Call when we receive a RTCP message with a ReceiveBlock. void UpdatePacketsLost(int64_t packets_lost, @@ -116,7 +119,8 @@ class SendSideBandwidthEstimation { int GetMinBitrate() const; void SetAcknowledgedRate(absl::optional acknowledged_rate, Timestamp at_time); - void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report); + void UpdateLossBasedEstimatorFromFeedbackVector( + const TransportPacketsFeedback& report); private: friend class GoogCcStatePrinter; @@ -199,6 +203,7 @@ class SendSideBandwidthEstimation { LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_; LossBasedBweV2 loss_based_bandwidth_estimator_v2_; FieldTrialFlag disable_receiver_limit_caps_only_; + BandwidthUsage delay_detector_state_; }; } // namespace webrtc #endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc index 85ce401098..e3db866cf7 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc @@ -10,6 +10,7 @@ #include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h" +#include "api/network_state_predictor.h" #include "api/rtc_event_log/rtc_event.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" #include "logging/rtc_event_log/mock/mock_rtc_event_log.h" @@ -54,7 +55,8 @@ void TestProbing(bool use_delay_based) { // Initial REMB applies immediately. if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kRembBps)); + DataRate::BitsPerSec(kRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kRembBps)); @@ -66,7 +68,8 @@ void TestProbing(bool use_delay_based) { now_ms += 2001; if (use_delay_based) { bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kSecondRembBps)); + DataRate::BitsPerSec(kSecondRembBps), + BandwidthUsage::kBwNormal); } else { bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), DataRate::BitsPerSec(kSecondRembBps)); @@ -157,7 +160,8 @@ TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) { Timestamp::Millis(now_ms)); bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), - DataRate::BitsPerSec(kDelayBasedBitrateBps)); + DataRate::BitsPerSec(kDelayBasedBitrateBps), + BandwidthUsage::kBwNormal); bwe.UpdateEstimate(Timestamp::Millis(now_ms)); EXPECT_GE(bwe.target_rate().bps(), kInitialBitrateBps); EXPECT_LE(bwe.target_rate().bps(), kDelayBasedBitrateBps); diff --git a/modules/congestion_controller/goog_cc/trendline_estimator.cc b/modules/congestion_controller/goog_cc/trendline_estimator.cc index 7fdf66c518..88182d4f80 100644 --- a/modules/congestion_controller/goog_cc/trendline_estimator.cc +++ b/modules/congestion_controller/goog_cc/trendline_estimator.cc @@ -34,8 +34,7 @@ constexpr double kDefaultTrendlineThresholdGain = 4.0; const char kBweWindowSizeInPacketsExperiment[] = "WebRTC-BweWindowSizeInPackets"; -size_t ReadTrendlineFilterWindowSize( - const WebRtcKeyValueConfig* key_value_config) { +size_t ReadTrendlineFilterWindowSize(const FieldTrialsView* key_value_config) { std::string experiment_string = key_value_config->Lookup(kBweWindowSizeInPacketsExperiment); size_t window_size; @@ -115,7 +114,7 @@ constexpr int kDeltaCounterMax = 1000; constexpr char TrendlineEstimatorSettings::kKey[]; TrendlineEstimatorSettings::TrendlineEstimatorSettings( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { if (absl::StartsWith( key_value_config->Lookup(kBweWindowSizeInPacketsExperiment), "Enabled")) { @@ -160,7 +159,7 @@ std::unique_ptr TrendlineEstimatorSettings::Parser() { } TrendlineEstimator::TrendlineEstimator( - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, NetworkStatePredictor* network_state_predictor) : settings_(key_value_config), smoothing_coef_(kDefaultTrendlineSmoothingCoeff), diff --git a/modules/congestion_controller/goog_cc/trendline_estimator.h b/modules/congestion_controller/goog_cc/trendline_estimator.h index 6fd442498b..ffda25df74 100644 --- a/modules/congestion_controller/goog_cc/trendline_estimator.h +++ b/modules/congestion_controller/goog_cc/trendline_estimator.h @@ -17,8 +17,8 @@ #include #include +#include "api/field_trials_view.h" #include "api/network_state_predictor.h" -#include "api/transport/webrtc_key_value_config.h" #include "modules/congestion_controller/goog_cc/delay_increase_detector_interface.h" #include "rtc_base/experiments/struct_parameters_parser.h" @@ -29,8 +29,7 @@ struct TrendlineEstimatorSettings { static constexpr unsigned kDefaultTrendlineWindowSize = 20; TrendlineEstimatorSettings() = delete; - explicit TrendlineEstimatorSettings( - const WebRtcKeyValueConfig* key_value_config); + explicit TrendlineEstimatorSettings(const FieldTrialsView* key_value_config); // Sort the packets in the window. Should be redundant, // but then almost no cost. @@ -51,7 +50,7 @@ struct TrendlineEstimatorSettings { class TrendlineEstimator : public DelayIncreaseDetectorInterface { public: - TrendlineEstimator(const WebRtcKeyValueConfig* key_value_config, + TrendlineEstimator(const FieldTrialsView* key_value_config, NetworkStatePredictor* network_state_predictor); ~TrendlineEstimator() override; diff --git a/modules/congestion_controller/pcc/BUILD.gn b/modules/congestion_controller/pcc/BUILD.gn index 38a3b8ad7c..85b12b3771 100644 --- a/modules/congestion_controller/pcc/BUILD.gn +++ b/modules/congestion_controller/pcc/BUILD.gn @@ -17,7 +17,6 @@ rtc_library("pcc") { ":pcc_controller", "../../../api/transport:network_control", "../../../api/units:time_delta", - "../../../rtc_base:rtc_base_approved", ] } @@ -36,7 +35,7 @@ rtc_library("pcc_controller") { "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:random", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -52,7 +51,7 @@ rtc_library("monitor_interval") { "../../../api/units:data_size", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", ] } @@ -65,7 +64,6 @@ rtc_library("rtt_tracker") { "../../../api/transport:network_control", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:rtc_base_approved", ] } @@ -79,7 +77,6 @@ rtc_library("utility_function") { "../../../api/transport:network_control", "../../../api/units:data_rate", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", ] } @@ -93,7 +90,6 @@ rtc_library("bitrate_controller") { ":utility_function", "../../../api/transport:network_control", "../../../api/units:data_rate", - "../../../rtc_base:rtc_base_approved", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -120,7 +116,6 @@ if (rtc_include_tests && !build_with_chromium) { "../../../api/units:data_size", "../../../api/units:time_delta", "../../../api/units:timestamp", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "../../../test/scenario", ] diff --git a/modules/congestion_controller/receive_side_congestion_controller_unittest.cc b/modules/congestion_controller/receive_side_congestion_controller_unittest.cc index 2aade06cbc..f2fd6d11d7 100644 --- a/modules/congestion_controller/receive_side_congestion_controller_unittest.cc +++ b/modules/congestion_controller/receive_side_congestion_controller_unittest.cc @@ -81,7 +81,7 @@ TEST(ReceiveSideCongestionControllerTest, } TEST(ReceiveSideCongestionControllerTest, ConvergesToCapacity) { - Scenario s("recieve_cc_unit/converge"); + Scenario s("receive_cc_unit/converge"); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::KilobitsPerSec(1000); net_conf.delay = TimeDelta::Millis(50); @@ -100,7 +100,7 @@ TEST(ReceiveSideCongestionControllerTest, ConvergesToCapacity) { } TEST(ReceiveSideCongestionControllerTest, IsFairToTCP) { - Scenario s("recieve_cc_unit/tcp_fairness"); + Scenario s("receive_cc_unit/tcp_fairness"); NetworkSimulationConfig net_conf; net_conf.bandwidth = DataRate::KilobitsPerSec(1000); net_conf.delay = TimeDelta::Millis(50); diff --git a/modules/congestion_controller/rtp/BUILD.gn b/modules/congestion_controller/rtp/BUILD.gn index 39d4d68192..7057cdfe42 100644 --- a/modules/congestion_controller/rtp/BUILD.gn +++ b/modules/congestion_controller/rtp/BUILD.gn @@ -30,6 +30,8 @@ rtc_library("control_handler") { "../../../api/units:data_size", "../../../api/units:time_delta", "../../../rtc_base:checks", + "../../../rtc_base:logging", + "../../../rtc_base:safe_conversions", "../../../rtc_base:safe_minmax", "../../../rtc_base/system:no_unique_address", "../../../system_wrappers:field_trial", @@ -58,7 +60,8 @@ rtc_library("transport_feedback") { "../../../api/units:timestamp", "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:macromagic", "../../../rtc_base/network:sent_packet", "../../../rtc_base/synchronization:mutex", "../../../rtc_base/system:no_unique_address", @@ -87,7 +90,7 @@ if (rtc_include_tests) { "../../../logging:mocks", "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:safe_conversions", "../../../rtc_base/network:sent_packet", "../../../system_wrappers", "../../../test:field_trial", diff --git a/modules/congestion_controller/rtp/transport_feedback_adapter.cc b/modules/congestion_controller/rtp/transport_feedback_adapter.cc index 87691bf263..87e917e978 100644 --- a/modules/congestion_controller/rtp/transport_feedback_adapter.cc +++ b/modules/congestion_controller/rtp/transport_feedback_adapter.cc @@ -22,7 +22,6 @@ #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 25ea1cb05a..3aa1b982b6 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/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("//build/config/linux/gtk/gtk.gni") import("//build/config/linux/pkg_config.gni") import("//build/config/ui.gni") import("//tools/generate_stubs/rules.gni") @@ -38,6 +39,7 @@ rtc_library("primitives") { deps = [ "../../api:scoped_refptr", "../../rtc_base:checks", + "../../rtc_base:refcount", "../../rtc_base/system:rtc_export", "//third_party/libyuv", ] @@ -58,6 +60,10 @@ if (rtc_include_tests) { "../../api:function_view", "../../api:scoped_refptr", "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:platform_thread", + "../../rtc_base:random", + "../../rtc_base:timeutils", ] if (rtc_desktop_capture_supported) { deps += [ @@ -65,7 +71,6 @@ if (rtc_include_tests) { ":primitives", ":screen_drawer", "../../rtc_base", - "../../rtc_base:rtc_base_approved", "../../rtc_base/third_party/base64", "../../system_wrappers", "../../test:test_support", @@ -77,6 +82,10 @@ if (rtc_include_tests) { "window_finder_unittest.cc", ] public_configs = [ ":x11_config" ] + + if (is_win) { + deps += [ "../../rtc_base/win:windows_version" ] + } } } @@ -101,32 +110,41 @@ if (rtc_include_tests) { "test_utils.h", "test_utils_unittest.cc", ] - if (is_win) { - sources += [ - "win/cursor_unittest.cc", - "win/cursor_unittest_resources.h", - "win/cursor_unittest_resources.rc", - "win/screen_capture_utils_unittest.cc", - "win/screen_capturer_win_directx_unittest.cc", - "win/test_support/test_window.cc", - "win/test_support/test_window.h", - "win/window_capture_utils_unittest.cc", - ] - } deps = [ ":desktop_capture", ":desktop_capture_mock", ":primitives", "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:random", + "../../rtc_base:timeutils", # TODO(bugs.webrtc.org/9987): Remove this dep on rtc_base:rtc_base once # rtc_base:threading is fully defined. "../../rtc_base:rtc_base", - "../../rtc_base:rtc_base_approved", "../../rtc_base:threading", "../../system_wrappers", "../../test:test_support", ] + + if (is_win) { + sources += [ + "win/cursor_unittest.cc", + "win/cursor_unittest_resources.h", + "win/cursor_unittest_resources.rc", + "win/screen_capture_utils_unittest.cc", + "win/screen_capturer_win_directx_unittest.cc", + "win/test_support/test_window.cc", + "win/test_support/test_window.h", + "win/window_capture_utils_unittest.cc", + ] + deps += [ + "../../rtc_base/win:scoped_com_initializer", + "../../rtc_base/win:windows_version", + ] + } + if (rtc_desktop_capture_supported) { sources += [ "screen_capturer_helper_unittest.cc", @@ -176,7 +194,7 @@ if (rtc_include_tests) { ":primitives", "../../api:scoped_refptr", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", "../../system_wrappers", ] @@ -200,7 +218,6 @@ if (rtc_include_tests) { deps = [ ":primitives", - "../../rtc_base:rtc_base_approved", "../../test:test_support", ] } @@ -228,6 +245,10 @@ if (is_linux || is_chromeos) { pkg_config("egl") { packages = [ "egl" ] } + pkg_config("epoxy") { + packages = [ "epoxy" ] + ignore_libs = true + } pkg_config("libdrm") { packages = [ "libdrm" ] if (!rtc_link_pipewire) { @@ -262,6 +283,13 @@ if (is_linux || is_chromeos) { if (!rtc_link_pipewire) { defines += [ "WEBRTC_DLOPEN_PIPEWIRE" ] } + + # Chromecast build config overrides `WEBRTC_USE_PIPEWIRE` even when + # `rtc_use_pipewire` is not set, which causes pipewire_config to not be + # included in targets. More details in: webrtc:13898 + if (is_linux && !is_chromecast) { + defines += [ "WEBRTC_USE_GIO" ] + } } } } @@ -298,9 +326,12 @@ if (is_mac) { ":desktop_capture_generic", ":primitives", "../../api:scoped_refptr", + "../../api:sequence_checker", "../../rtc_base", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:timeutils", "../../rtc_base/system:rtc_export", "../../sdk:helpers_objc", ] @@ -365,7 +396,9 @@ rtc_library("desktop_capture_generic") { "window_finder.cc", "window_finder.h", ] - + if (is_linux && !is_chromecast && rtc_use_pipewire) { + sources += [ "desktop_capture_metadata.h" ] + } if (is_mac) { sources += [ "mac/desktop_configuration.h", @@ -391,7 +424,6 @@ rtc_library("desktop_capture_generic") { ] } } - if (rtc_use_x11_extensions || rtc_use_pipewire) { sources += [ "mouse_cursor_monitor_linux.cc", @@ -462,8 +494,16 @@ rtc_library("desktop_capture_generic") { "../../api:sequence_checker", "../../rtc_base", # TODO(kjellander): Cleanup in bugs.webrtc.org/3806. "../../rtc_base:checks", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:random", + "../../rtc_base:refcount", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:arch", + "../../rtc_base/system:no_unique_address", "../../rtc_base/system:rtc_export", "../../system_wrappers", "../../system_wrappers:metrics", @@ -530,8 +570,10 @@ rtc_library("desktop_capture_generic") { "dxgi.lib", ] deps += [ - "../../rtc_base:rtc_base_approved", "../../rtc_base:win32", + "../../rtc_base/win:create_direct3d_device", + "../../rtc_base/win:get_activation_factory", + "../../rtc_base/win:windows_version", ] } @@ -545,9 +587,7 @@ rtc_library("desktop_capture_generic") { deps += [ "../../rtc_base:sanitizer" ] } - if (build_with_mozilla) { - deps += [ "../../rtc_base:rtc_base_approved" ] - } else { + if (!build_with_mozilla) { deps += [ "//third_party/libyuv" ] } @@ -563,12 +603,20 @@ rtc_library("desktop_capture_generic") { "linux/wayland/egl_dmabuf.h", "linux/wayland/mouse_cursor_monitor_pipewire.cc", "linux/wayland/mouse_cursor_monitor_pipewire.h", + "linux/wayland/portal_request_response.h", "linux/wayland/scoped_glib.cc", "linux/wayland/scoped_glib.h", + "linux/wayland/screen_capture_portal_interface.cc", + "linux/wayland/screen_capture_portal_interface.h", "linux/wayland/screencast_portal.cc", "linux/wayland/screencast_portal.h", + "linux/wayland/screencast_stream_utils.cc", + "linux/wayland/screencast_stream_utils.h", "linux/wayland/shared_screencast_stream.cc", "linux/wayland/shared_screencast_stream.h", + "linux/wayland/xdg_desktop_portal_utils.cc", + "linux/wayland/xdg_desktop_portal_utils.h", + "linux/wayland/xdg_session_details.h", ] configs += [ @@ -576,6 +624,7 @@ rtc_library("desktop_capture_generic") { ":pipewire", ":gbm", ":egl", + ":epoxy", ":libdrm", ] @@ -599,6 +648,8 @@ rtc_library("desktop_capture_generic") { "win/wgc_desktop_frame.cc", "win/wgc_desktop_frame.h", ] + libs += [ "dwmapi.lib" ] + deps += [ "../../rtc_base/win:hstring" ] } } diff --git a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc index ca3a89f49b..c20843414b 100644 --- a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc +++ b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc @@ -23,8 +23,11 @@ namespace webrtc { BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper( std::unique_ptr capturer, - RgbaColor blank_pixel) - : capturer_(std::move(capturer)), blank_pixel_(blank_pixel) { + RgbaColor blank_pixel, + bool check_per_capture) + : capturer_(std::move(capturer)), + blank_pixel_(blank_pixel), + check_per_capture_(check_per_capture) { RTC_DCHECK(capturer_); } @@ -56,6 +59,13 @@ bool BlankDetectorDesktopCapturerWrapper::GetSourceList(SourceList* sources) { } bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) { + if (check_per_capture_) { + // If we start capturing a new source, we must reset these members + // so we don't short circuit the blank detection logic. + is_first_frame_ = true; + non_blank_frame_received_ = false; + } + return capturer_->SelectSource(id); } diff --git a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h index f5c2ce201b..d10f9cf725 100644 --- a/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h +++ b/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h @@ -34,7 +34,8 @@ class BlankDetectorDesktopCapturerWrapper final // takes ownership of `capturer`. The `blank_pixel` is the unmodified color // returned by the `capturer`. BlankDetectorDesktopCapturerWrapper(std::unique_ptr capturer, - RgbaColor blank_pixel); + RgbaColor blank_pixel, + bool check_per_capture = false); ~BlankDetectorDesktopCapturerWrapper() override; // DesktopCapturer interface. @@ -70,6 +71,10 @@ class BlankDetectorDesktopCapturerWrapper final // Whether current frame is the first frame. bool is_first_frame_ = true; + // Blank inspection is made per capture instead of once for all + // screens or windows. + bool check_per_capture_ = false; + DesktopCapturer::Callback* callback_ = nullptr; }; diff --git a/modules/desktop_capture/desktop_and_cursor_composer.cc b/modules/desktop_capture/desktop_and_cursor_composer.cc index 7ca0af038c..7ff983c2d1 100644 --- a/modules/desktop_capture/desktop_and_cursor_composer.cc +++ b/modules/desktop_capture/desktop_and_cursor_composer.cc @@ -105,7 +105,11 @@ DesktopFrameWithCursor::DesktopFrameWithCursor( if (!previous_cursor_rect.equals(cursor_rect_)) { mutable_updated_region()->AddRect(cursor_rect_); - mutable_updated_region()->AddRect(previous_cursor_rect); + + // Only add the previous cursor if it is inside the frame. + // (it can be outside the frame if the desktop has been resized). + if (original_frame_->rect().ContainsRect(previous_cursor_rect)) + mutable_updated_region()->AddRect(previous_cursor_rect); } else if (cursor_changed) { mutable_updated_region()->AddRect(cursor_rect_); } @@ -203,6 +207,12 @@ bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) { return desktop_capturer_->IsOccluded(pos); } +#if defined(WEBRTC_USE_GIO) +DesktopCaptureMetadata DesktopAndCursorComposer::GetMetadata() { + return desktop_capturer_->GetMetadata(); +} +#endif // defined(WEBRTC_USE_GIO) + void DesktopAndCursorComposer::OnCaptureResult( DesktopCapturer::Result result, std::unique_ptr frame) { @@ -212,7 +222,7 @@ void DesktopAndCursorComposer::OnCaptureResult( !desktop_capturer_->IsOccluded(cursor_position_)) { DesktopVector relative_position = cursor_position_.subtract(frame->top_left()); -#if defined(WEBRTC_MAC) +#if defined(WEBRTC_MAC) || defined(CHROMEOS) // On OSX, the logical(DIP) and physical coordinates are used mixingly. // For example, the captured cursor has its size in physical pixels(2x) // and location in logical(DIP) pixels on Retina monitor. This will cause diff --git a/modules/desktop_capture/desktop_and_cursor_composer.h b/modules/desktop_capture/desktop_and_cursor_composer.h index edb764d168..a078b3eeef 100644 --- a/modules/desktop_capture/desktop_and_cursor_composer.h +++ b/modules/desktop_capture/desktop_and_cursor_composer.h @@ -12,7 +12,9 @@ #define MODULES_DESKTOP_CAPTURE_DESKTOP_AND_CURSOR_COMPOSER_H_ #include - +#if defined(WEBRTC_USE_GIO) +#include "modules/desktop_capture/desktop_capture_metadata.h" +#endif // defined(WEBRTC_USE_GIO) #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_capturer.h" @@ -59,6 +61,9 @@ class RTC_EXPORT DesktopAndCursorComposer bool SelectSource(SourceId id) override; bool FocusOnSelectedSource() override; bool IsOccluded(const DesktopVector& pos) override; +#if defined(WEBRTC_USE_GIO) + DesktopCaptureMetadata GetMetadata() override; +#endif // defined(WEBRTC_USE_GIO) // MouseCursorMonitor::Callback interface. void OnMouseCursor(MouseCursor* cursor) override; diff --git a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc index 5596576d9e..4d879a740e 100644 --- a/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc +++ b/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc @@ -15,18 +15,22 @@ #include #include +#include #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/shared_desktop_frame.h" #include "rtc_base/arraysize.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { +using testing::ElementsAre; + const int kFrameXCoord = 100; const int kFrameYCoord = 200; const int kScreenWidth = 100; @@ -75,12 +79,12 @@ uint32_t BlendPixels(uint32_t dest, uint32_t src) { return b + (g << 8) + (r << 16) + 0xff000000; } -DesktopFrame* CreateTestFrame() { - DesktopFrame* frame = - new BasicDesktopFrame(DesktopSize(kScreenWidth, kScreenHeight)); +DesktopFrame* CreateTestFrame(int width = kScreenWidth, + int height = kScreenHeight) { + DesktopFrame* frame = new BasicDesktopFrame(DesktopSize(width, height)); uint32_t* data = reinterpret_cast(frame->data()); - for (int y = 0; y < kScreenHeight; ++y) { - for (int x = 0; x < kScreenWidth; ++x) { + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { *(data++) = GetFakeFramePixelValue(DesktopVector(x, y)); } } @@ -101,6 +105,15 @@ MouseCursor* CreateTestCursor(DesktopVector hotspot) { return new MouseCursor(image.release(), hotspot); } +std::vector GetUpdatedRegions(const DesktopFrame& frame) { + std::vector result; + for (webrtc::DesktopRegion::Iterator i(frame.updated_region()); !i.IsAtEnd(); + i.Advance()) { + result.push_back(i.rect()); + } + return result; +} + class FakeScreenCapturer : public DesktopCapturer { public: FakeScreenCapturer() {} @@ -184,10 +197,20 @@ void VerifyFrame(const DesktopFrame& frame, } // namespace +bool operator==(const DesktopRect& left, const DesktopRect& right) { + return left.equals(right); +} + +std::ostream& operator<<(std::ostream& out, const DesktopRect& rect) { + out << "{" << rect.left() << "+" << rect.top() << "-" << rect.width() << "x" + << rect.height() << "}"; + return out; +} + class DesktopAndCursorComposerTest : public ::testing::Test, public DesktopCapturer::Callback { public: - DesktopAndCursorComposerTest(bool include_cursor = true) + explicit DesktopAndCursorComposerTest(bool include_cursor = true) : fake_screen_(new FakeScreenCapturer()), fake_cursor_(include_cursor ? new FakeMouseMonitor() : nullptr), blender_(fake_screen_, fake_cursor_) { @@ -413,6 +436,32 @@ TEST_F(DesktopAndCursorComposerNoCursorMonitorTest, EXPECT_TRUE(frame_->updated_region().Equals(expected_region)); } +TEST_F(DesktopAndCursorComposerNoCursorMonitorTest, + UpdatedRegionDoesNotIncludeOldCursorIfOutOfBounds) { + blender_.OnMouseCursor(CreateTestCursor(DesktopVector(0, 0))); + + std::unique_ptr first_frame( + SharedDesktopFrame::Wrap(CreateTestFrame(1000, 1000))); + blender_.OnMouseCursorPosition(DesktopVector(900, 900)); + fake_screen_->SetNextFrame(first_frame->Share()); + + blender_.CaptureFrame(); + + // Second frame is smaller than first frame, and the first cursor is outside + // of the bounds of the new frame, so it should not be in the updated region. + std::unique_ptr second_frame( + SharedDesktopFrame::Wrap(CreateTestFrame(500, 500))); + auto second_cursor_rect = + DesktopRect::MakeXYWH(400, 400, kCursorWidth, kCursorHeight); + blender_.OnMouseCursorPosition(DesktopVector(400, 400)); + fake_screen_->SetNextFrame(second_frame->Share()); + blender_.CaptureFrame(); + + DesktopRegion expected_region; + expected_region.AddRect(second_cursor_rect); + EXPECT_THAT(GetUpdatedRegions(*frame_), ElementsAre(second_cursor_rect)); +} + TEST_F(DesktopAndCursorComposerNoCursorMonitorTest, UpdatedRegionIncludesOldAndNewCursorRectsIfShapeChanged) { std::unique_ptr frame( diff --git a/modules/desktop_capture/desktop_capture_metadata.h b/modules/desktop_capture/desktop_capture_metadata.h new file mode 100644 index 0000000000..faca156e33 --- /dev/null +++ b/modules/desktop_capture/desktop_capture_metadata.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 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_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_ +#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_ + +#if defined(WEBRTC_USE_GIO) +#include "modules/desktop_capture/linux/wayland/xdg_session_details.h" +#endif // defined(WEBRTC_USE_GIO) + +namespace webrtc { + +// Container for the metadata associated with a desktop capturer. +struct DesktopCaptureMetadata { +#if defined(WEBRTC_USE_GIO) + // Details about the XDG desktop session handle (used by wayland + // implementation in remoting) + xdg_portal::SessionDetails session_details; +#endif // defined(WEBRTC_USE_GIO) +}; + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_ diff --git a/modules/desktop_capture/desktop_capture_options.h b/modules/desktop_capture/desktop_capture_options.h index c6bc52f8a7..dd3cde0145 100644 --- a/modules/desktop_capture/desktop_capture_options.h +++ b/modules/desktop_capture/desktop_capture_options.h @@ -163,6 +163,17 @@ class RTC_EXPORT DesktopCaptureOptions { // precedence over the cropping, directx, and magnification flags. bool allow_wgc_capturer() const { return allow_wgc_capturer_; } void set_allow_wgc_capturer(bool allow) { allow_wgc_capturer_ = allow; } + + // This flag enables the WGC capturer for fallback capturer. + // The flag is useful when the first capturer (eg. WindowCapturerWinGdi) is + // unreliable in certain devices where WGC is supported, but not used by + // default. + bool allow_wgc_capturer_fallback() const { + return allow_wgc_capturer_fallback_; + } + void set_allow_wgc_capturer_fallback(bool allow) { + allow_wgc_capturer_fallback_ = allow; + } #endif // defined(RTC_ENABLE_WIN_WGC) #endif // defined(WEBRTC_WIN) @@ -203,6 +214,7 @@ class RTC_EXPORT DesktopCaptureOptions { bool allow_cropping_window_capturer_ = false; #if defined(RTC_ENABLE_WIN_WGC) bool allow_wgc_capturer_ = false; + bool allow_wgc_capturer_fallback_ = false; #endif #endif #if defined(WEBRTC_USE_X11) diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h index bc26db7cc4..f2aad8fd6f 100644 --- a/modules/desktop_capture/desktop_capture_types.h +++ b/modules/desktop_capture/desktop_capture_types.h @@ -15,6 +15,8 @@ namespace webrtc { +enum class CaptureType { kWindow, kScreen }; + // Type used to identify windows on the desktop. Values are platform-specific: // - On Windows: HWND cast to intptr_t. // - On Linux (with X11): X11 Window (unsigned long) type cast to intptr_t. diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc index 9e6b99ac58..f4676b7fe2 100644 --- a/modules/desktop_capture/desktop_capturer.cc +++ b/modules/desktop_capture/desktop_capturer.cc @@ -54,8 +54,7 @@ bool DesktopCapturer::IsOccluded(const DesktopVector& pos) { std::unique_ptr DesktopCapturer::CreateWindowCapturer( const DesktopCaptureOptions& options) { #if defined(RTC_ENABLE_WIN_WGC) - if (options.allow_wgc_capturer() && - rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_RS5) { + if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kWindow)) { return WgcCapturerWin::CreateRawWindowCapturer(options); } #endif // defined(RTC_ENABLE_WIN_WGC) @@ -78,8 +77,7 @@ std::unique_ptr DesktopCapturer::CreateWindowCapturer( std::unique_ptr DesktopCapturer::CreateScreenCapturer( const DesktopCaptureOptions& options) { #if defined(RTC_ENABLE_WIN_WGC) - if (options.allow_wgc_capturer() && - rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10_20H1) { + if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kScreen)) { return WgcCapturerWin::CreateRawScreenCapturer(options); } #endif // defined(RTC_ENABLE_WIN_WGC) diff --git a/modules/desktop_capture/desktop_capturer.h b/modules/desktop_capture/desktop_capturer.h index 822a75d947..d3164258ec 100644 --- a/modules/desktop_capture/desktop_capturer.h +++ b/modules/desktop_capture/desktop_capturer.h @@ -19,6 +19,9 @@ #include #include +#if defined(WEBRTC_USE_GIO) +#include "modules/desktop_capture/desktop_capture_metadata.h" +#endif // defined(WEBRTC_USE_GIO) #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_frame.h" #include "modules/desktop_capture/shared_memory.h" @@ -143,6 +146,12 @@ class RTC_EXPORT DesktopCapturer { static bool IsRunningUnderWayland(); #endif // defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11) +#if defined(WEBRTC_USE_GIO) + // Populates implementation specific metadata into the passed in pointer. + // Classes can choose to override it or use the default no-op implementation. + virtual DesktopCaptureMetadata GetMetadata() { return {}; } +#endif // defined(WEBRTC_USE_GIO) + protected: // CroppingWindowCapturer needs to create raw capturers without wrappers, so // the following two functions are protected. diff --git a/modules/desktop_capture/desktop_capturer_differ_wrapper.cc b/modules/desktop_capture/desktop_capturer_differ_wrapper.cc index 916d76f7ce..77543e4060 100644 --- a/modules/desktop_capture/desktop_capturer_differ_wrapper.cc +++ b/modules/desktop_capture/desktop_capturer_differ_wrapper.cc @@ -186,6 +186,12 @@ bool DesktopCapturerDifferWrapper::IsOccluded(const DesktopVector& pos) { return base_capturer_->IsOccluded(pos); } +#if defined(WEBRTC_USE_GIO) +DesktopCaptureMetadata DesktopCapturerDifferWrapper::GetMetadata() { + return base_capturer_->GetMetadata(); +} +#endif // defined(WEBRTC_USE_GIO) + void DesktopCapturerDifferWrapper::OnCaptureResult( Result result, std::unique_ptr input_frame) { diff --git a/modules/desktop_capture/desktop_capturer_differ_wrapper.h b/modules/desktop_capture/desktop_capturer_differ_wrapper.h index 1f70cef186..6ebb5d7bc3 100644 --- a/modules/desktop_capture/desktop_capturer_differ_wrapper.h +++ b/modules/desktop_capture/desktop_capturer_differ_wrapper.h @@ -12,7 +12,9 @@ #define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_ #include - +#if defined(WEBRTC_USE_GIO) +#include "modules/desktop_capture/desktop_capture_metadata.h" +#endif // defined(WEBRTC_USE_GIO) #include "modules/desktop_capture/desktop_capture_types.h" #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/desktop_frame.h" @@ -52,7 +54,9 @@ class RTC_EXPORT DesktopCapturerDifferWrapper bool SelectSource(SourceId id) override; bool FocusOnSelectedSource() override; bool IsOccluded(const DesktopVector& pos) override; - +#if defined(WEBRTC_USE_GIO) + DesktopCaptureMetadata GetMetadata() override; +#endif // defined(WEBRTC_USE_GIO) private: // DesktopCapturer::Callback interface. void OnCaptureResult(Result result, diff --git a/modules/desktop_capture/desktop_frame.cc b/modules/desktop_capture/desktop_frame.cc index 9e4a899fd2..837cbfca89 100644 --- a/modules/desktop_capture/desktop_frame.cc +++ b/modules/desktop_capture/desktop_frame.cc @@ -45,9 +45,13 @@ void DesktopFrame::CopyPixelsFrom(const uint8_t* src_buffer, RTC_CHECK(DesktopRect::MakeSize(size()).ContainsRect(dest_rect)); uint8_t* dest = GetFrameDataAtPos(dest_rect.top_left()); - libyuv::CopyPlane(src_buffer, src_stride, dest, stride(), - DesktopFrame::kBytesPerPixel * dest_rect.width(), - dest_rect.height()); + // TODO(crbug.com/1330019): Temporary workaround for a known libyuv crash when + // the height or width is 0. Remove this once this change has been merged. + if (dest_rect.width() && dest_rect.height()) { + libyuv::CopyPlane(src_buffer, src_stride, dest, stride(), + DesktopFrame::kBytesPerPixel * dest_rect.width(), + dest_rect.height()); + } } void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame, @@ -112,7 +116,7 @@ DesktopRect DesktopFrame::rect() const { float DesktopFrame::scale_factor() const { float scale = 1.0f; -#if defined(WEBRTC_MAC) +#if defined(WEBRTC_MAC) || defined(CHROMEOS) // At least on Windows the logical and physical pixel are the same // See http://crbug.com/948362. if (!dpi().is_zero() && dpi().x() == dpi().y()) @@ -157,9 +161,13 @@ BasicDesktopFrame::~BasicDesktopFrame() { // static DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) { DesktopFrame* result = new BasicDesktopFrame(frame.size()); - libyuv::CopyPlane(frame.data(), frame.stride(), result->data(), - result->stride(), frame.size().width() * kBytesPerPixel, - frame.size().height()); + // TODO(crbug.com/1330019): Temporary workaround for a known libyuv crash when + // the height or width is 0. Remove this once this change has been merged. + if (frame.size().width() && frame.size().height()) { + libyuv::CopyPlane(frame.data(), frame.stride(), result->data(), + result->stride(), frame.size().width() * kBytesPerPixel, + frame.size().height()); + } result->CopyFrameInfoFrom(frame); return result; } diff --git a/modules/desktop_capture/differ_block.cc b/modules/desktop_capture/differ_block.cc index 4f0c5430c9..54ee0829ea 100644 --- a/modules/desktop_capture/differ_block.cc +++ b/modules/desktop_capture/differ_block.cc @@ -30,11 +30,7 @@ bool VectorDifference(const uint8_t* image1, const uint8_t* image2) { static bool (*diff_proc)(const uint8_t*, const uint8_t*) = nullptr; if (!diff_proc) { -#if defined(WEBRTC_ARCH_ARM_FAMILY) || defined(WEBRTC_ARCH_MIPS_FAMILY) - // For ARM and MIPS processors, always use C version. - // TODO(hclam): Implement a NEON version. - diff_proc = &VectorDifference_C; -#else +#if defined(WEBRTC_ARCH_X86_FAMILY) bool have_sse2 = GetCPUInfo(kSSE2) != 0; // For x86 processors, check if SSE2 is supported. if (have_sse2 && kBlockSize == 32) { @@ -44,6 +40,10 @@ bool VectorDifference(const uint8_t* image1, const uint8_t* image2) { } else { diff_proc = &VectorDifference_C; } +#else + // For other processors, always use C version. + // TODO(hclam): Implement a NEON version. + diff_proc = &VectorDifference_C; #endif } diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc index b6120ee68c..0fc90e0e8d 100644 --- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc @@ -12,24 +12,38 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" namespace webrtc { +namespace { + +using xdg_portal::RequestResponse; +using xdg_portal::ScreenCapturePortalInterface; +using xdg_portal::SessionDetails; + +} // namespace + BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options) - : options_(options) { - screencast_portal_ = std::make_unique( - ScreenCastPortal::CaptureSourceType::kAnyScreenContent, this); -} + : BaseCapturerPipeWire( + options, + std::make_unique( + ScreenCastPortal::CaptureSourceType::kAnyScreenContent, + this)) {} + +BaseCapturerPipeWire::BaseCapturerPipeWire( + const DesktopCaptureOptions& options, + std::unique_ptr portal) + : options_(options), portal_(std::move(portal)) {} BaseCapturerPipeWire::~BaseCapturerPipeWire() {} -void BaseCapturerPipeWire::OnScreenCastRequestResult( - ScreenCastPortal::RequestResponse result, - uint32_t stream_node_id, - int fd) { - if (result != ScreenCastPortal::RequestResponse::kSuccess || +void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result, + uint32_t stream_node_id, + int fd) { + if (result != RequestResponse::kSuccess || !options_.screencast_stream()->StartScreenCastStream(stream_node_id, fd)) { capturer_failed_ = true; @@ -50,7 +64,7 @@ void BaseCapturerPipeWire::Start(Callback* callback) { callback_ = callback; - screencast_portal_->Start(); + portal_->Start(); } void BaseCapturerPipeWire::CaptureFrame() { @@ -91,4 +105,8 @@ bool BaseCapturerPipeWire::SelectSource(SourceId id) { return true; } +SessionDetails BaseCapturerPipeWire::GetSessionDetails() { + return portal_->GetSessionDetails(); +} + } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h index 7663cb60ae..b6f4ba8a1a 100644 --- a/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h +++ b/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h @@ -13,15 +13,22 @@ #include "modules/desktop_capture/desktop_capture_options.h" #include "modules/desktop_capture/desktop_capturer.h" +#include "modules/desktop_capture/linux/wayland/portal_request_response.h" +#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h" #include "modules/desktop_capture/linux/wayland/screencast_portal.h" #include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" +#include "modules/desktop_capture/linux/wayland/xdg_session_details.h" namespace webrtc { class BaseCapturerPipeWire : public DesktopCapturer, public ScreenCastPortal::PortalNotifier { public: - BaseCapturerPipeWire(const DesktopCaptureOptions& options); + explicit BaseCapturerPipeWire(const DesktopCaptureOptions& options); + BaseCapturerPipeWire( + const DesktopCaptureOptions& options, + std::unique_ptr portal); ~BaseCapturerPipeWire() override; BaseCapturerPipeWire(const BaseCapturerPipeWire&) = delete; @@ -34,16 +41,18 @@ class BaseCapturerPipeWire : public DesktopCapturer, bool SelectSource(SourceId id) override; // ScreenCastPortal::PortalNotifier interface. - void OnScreenCastRequestResult(ScreenCastPortal::RequestResponse result, + void OnScreenCastRequestResult(xdg_portal::RequestResponse result, uint32_t stream_node_id, int fd) override; void OnScreenCastSessionClosed() override; + xdg_portal::SessionDetails GetSessionDetails(); + private: DesktopCaptureOptions options_ = {}; Callback* callback_ = nullptr; bool capturer_failed_ = false; - std::unique_ptr screencast_portal_; + std::unique_ptr portal_; }; } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h index 8af4f879f8..da670bece9 100644 --- a/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h +++ b/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h @@ -20,6 +20,7 @@ #include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h" #include "modules/desktop_capture/mouse_cursor.h" #include "modules/desktop_capture/mouse_cursor_monitor.h" +#include "rtc_base/system/no_unique_address.h" namespace webrtc { diff --git a/modules/desktop_capture/linux/wayland/portal_request_response.h b/modules/desktop_capture/linux/wayland/portal_request_response.h new file mode 100644 index 0000000000..dde9ac5eff --- /dev/null +++ b/modules/desktop_capture/linux/wayland/portal_request_response.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 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_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_ + +namespace webrtc { +namespace xdg_portal { + +// Contains type of responses that can be observed when making a request to +// a desktop portal interface. +enum class RequestResponse { + // Unknown, the initialized status. + kUnknown, + // Success, the request is carried out. + kSuccess, + // The user cancelled the interaction. + kUserCancelled, + // The user interaction was ended in some other way. + kError, + + kMaxValue = kError, +}; + +} // namespace xdg_portal +} // namespace webrtc +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_ diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc new file mode 100644 index 0000000000..c76fa73dad --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc @@ -0,0 +1,118 @@ +/* + * Copyright 2022 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/desktop_capture/linux/wayland/screen_capture_portal_interface.h" + +#include + +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +void ScreenCapturePortalInterface::RequestSessionUsingProxy( + GAsyncResult* result) { + Scoped error; + GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); + if (!proxy) { + // Ignore the error caused by user cancelling the request via `cancellable_` + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: " + << error->message; + OnPortalDone(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Successfully created proxy for the portal."; + RequestSession(proxy); +} + +void ScreenCapturePortalInterface::OnSessionRequestResult( + GDBusProxy* proxy, + GAsyncResult* result) { + Scoped error; + Scoped variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + // Ignore the error caused by user cancelling the request via `cancellable_` + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to request session: " << error->message; + OnPortalDone(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Initializing the session."; + + Scoped handle; + g_variant_get_child(variant.get(), /*index=*/0, /*format_string=*/"o", + &handle); + if (!handle) { + RTC_LOG(LS_ERROR) << "Failed to initialize the session."; + OnPortalDone(RequestResponse::kError); + return; + } +} + +void ScreenCapturePortalInterface::RegisterSessionClosedSignalHandler( + const SessionClosedSignalHandler session_close_signal_handler, + GVariant* parameters, + GDBusConnection* connection, + std::string& session_handle, + guint& session_closed_signal_id) { + uint32_t portal_response; + Scoped response_data; + g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response, + response_data.receive()); + Scoped g_session_handle( + g_variant_lookup_value(response_data.get(), /*key=*/"session_handle", + /*expected_type=*/nullptr)); + session_handle = g_variant_dup_string( + /*value=*/g_session_handle.get(), /*length=*/nullptr); + + if (session_handle.empty() || portal_response) { + RTC_LOG(LS_ERROR) << "Failed to request the session subscription."; + OnPortalDone(RequestResponse::kError); + return; + } + + session_closed_signal_id = g_dbus_connection_signal_subscribe( + connection, kDesktopBusName, kSessionInterfaceName, /*member=*/"Closed", + session_handle.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE, + session_close_signal_handler, this, /*user_data_free_func=*/nullptr); +} + +void ScreenCapturePortalInterface::OnStartRequestResult(GDBusProxy* proxy, + GAsyncResult* result) { + Scoped error; + Scoped variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to start the portal session: " + << error->message; + OnPortalDone(RequestResponse::kError); + return; + } + + Scoped handle; + g_variant_get_child(variant.get(), 0, "o", handle.receive()); + if (!handle) { + RTC_LOG(LS_ERROR) << "Failed to initialize the start portal session."; + OnPortalDone(RequestResponse::kError); + return; + } + + RTC_LOG(LS_INFO) << "Subscribed to the start signal."; +} + +} // namespace xdg_portal +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h new file mode 100644 index 0000000000..775ed1facc --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 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_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_ + +#include + +#include + +#include "modules/desktop_capture/linux/wayland/portal_request_response.h" +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" +#include "modules/desktop_capture/linux/wayland/xdg_session_details.h" + +namespace webrtc { +namespace xdg_portal { + +using SessionClosedSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); + +// A base class for XDG desktop portals that can capture desktop/screen. +// Note: downstream clients inherit from this class so it is advisable to +// provide a default implementation of any new virtual methods that may be added +// to this class. +class ScreenCapturePortalInterface { + public: + virtual ~ScreenCapturePortalInterface() {} + // Gets details about the session such as session handle. + virtual xdg_portal::SessionDetails GetSessionDetails() { return {}; } + // Starts the portal setup. + virtual void Start() {} + // Notifies observers about the success/fail state of the portal + // request/response. + virtual void OnPortalDone(xdg_portal::RequestResponse result) {} + // Sends a create session request to the portal. + virtual void RequestSession(GDBusProxy* proxy) {} + + // Following methods should not be made virtual as they share a common + // implementation between portals. + + // Requests portal session using the proxy object. + void RequestSessionUsingProxy(GAsyncResult* result); + // Handles the session request result. + void OnSessionRequestResult(GDBusProxy* proxy, GAsyncResult* result); + // Subscribes to session close signal and sets up a handler for it. + void RegisterSessionClosedSignalHandler( + const SessionClosedSignalHandler session_close_signal_handler, + GVariant* parameters, + GDBusConnection* connection, + std::string& session_handle, + guint& session_closed_signal_id); + // Handles the result of session start request. + void OnStartRequestResult(GDBusProxy* proxy, GAsyncResult* result); +}; + +} // namespace xdg_portal +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_ diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.cc b/modules/desktop_capture/linux/wayland/screencast_portal.cc index ac0e9700ed..c70628bcf2 100644 --- a/modules/desktop_capture/linux/wayland/screencast_portal.cc +++ b/modules/desktop_capture/linux/wayland/screencast_portal.cc @@ -14,189 +14,137 @@ #include #include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" namespace webrtc { +namespace { + +using xdg_portal::kScreenCastInterfaceName; +using xdg_portal::PrepareSignalHandle; +using xdg_portal::RequestResponse; +using xdg_portal::RequestSessionProxy; +using xdg_portal::SetupRequestResponseSignal; +using xdg_portal::SetupSessionRequestHandlers; +using xdg_portal::StartSessionRequest; +using xdg_portal::TearDownSession; + +} // namespace + +ScreenCastPortal::ScreenCastPortal( + ScreenCastPortal::CaptureSourceType source_type, + PortalNotifier* notifier) + : ScreenCastPortal(source_type, + notifier, + OnProxyRequested, + OnSourcesRequestResponseSignal, + this) {} + +ScreenCastPortal::ScreenCastPortal( + CaptureSourceType source_type, + PortalNotifier* notifier, + ProxyRequestResponseHandler proxy_request_response_handler, + SourcesRequestResponseSignalHandler sources_request_response_signal_handler, + gpointer user_data) + : notifier_(notifier), + capture_source_type_(source_type), + proxy_request_response_handler_(proxy_request_response_handler), + sources_request_response_signal_handler_( + sources_request_response_signal_handler), + user_data_(user_data) {} -const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; -const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; -const char kDesktopRequestObjectPath[] = - "/org/freedesktop/portal/desktop/request"; -const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; -const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; -const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; +ScreenCastPortal::~ScreenCastPortal() { + Cleanup(); +} -ScreenCastPortal::ScreenCastPortal(CaptureSourceType source_type, - PortalNotifier* notifier) - : notifier_(notifier), capture_source_type_(source_type) {} +void ScreenCastPortal::Cleanup() { + UnsubscribeSignalHandlers(); + TearDownSession(std::move(session_handle_), proxy_, cancellable_, + connection_); + session_handle_ = ""; + cancellable_ = nullptr; + proxy_ = nullptr; -ScreenCastPortal::~ScreenCastPortal() { + if (pw_fd_ != -1) { + close(pw_fd_); + } +} + +void ScreenCastPortal::UnsubscribeSignalHandlers() { if (start_request_signal_id_) { g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); + start_request_signal_id_ = 0; } + if (sources_request_signal_id_) { g_dbus_connection_signal_unsubscribe(connection_, sources_request_signal_id_); + sources_request_signal_id_ = 0; } + if (session_request_signal_id_) { g_dbus_connection_signal_unsubscribe(connection_, session_request_signal_id_); + session_request_signal_id_ = 0; } +} - if (!session_handle_.empty()) { - Scoped message( - g_dbus_message_new_method_call(kDesktopBusName, session_handle_.c_str(), - kSessionInterfaceName, "Close")); - if (message.get()) { - Scoped error; - g_dbus_connection_send_message(connection_, message.get(), - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - /*out_serial=*/nullptr, error.receive()); - if (error.get()) { - RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; - } - } +void ScreenCastPortal::SetSessionDetails( + const xdg_portal::SessionDetails& session_details) { + if (session_details.proxy) { + proxy_ = session_details.proxy; + connection_ = g_dbus_proxy_get_connection(proxy_); } - - if (cancellable_) { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); - cancellable_ = nullptr; + if (session_details.cancellable) { + cancellable_ = session_details.cancellable; } - - if (proxy_) { - g_object_unref(proxy_); - proxy_ = nullptr; + if (!session_details.session_handle.empty()) { + session_handle_ = session_details.session_handle; } - - if (pw_fd_ != -1) { - close(pw_fd_); + if (session_details.pipewire_stream_node_id) { + pw_stream_node_id_ = session_details.pipewire_stream_node_id; } } void ScreenCastPortal::Start() { cancellable_ = g_cancellable_new(); - g_dbus_proxy_new_for_bus( - G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, - kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, - cancellable_, reinterpret_cast(OnProxyRequested), - this); + RequestSessionProxy(kScreenCastInterfaceName, proxy_request_response_handler_, + cancellable_, this); } -void ScreenCastPortal::PortalFailed(RequestResponse result) { - notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_); +xdg_portal::SessionDetails ScreenCastPortal::GetSessionDetails() { + return {}; // No-op } -uint32_t ScreenCastPortal::SetupRequestResponseSignal( - const char* object_path, - GDBusSignalCallback callback) { - return g_dbus_connection_signal_subscribe( - connection_, kDesktopBusName, kRequestInterfaceName, "Response", - object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, - callback, this, /*user_data_free_func=*/nullptr); +void ScreenCastPortal::OnPortalDone(RequestResponse result) { + notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_); + if (result != RequestResponse::kSuccess) { + Cleanup(); + } } // static -void ScreenCastPortal::OnProxyRequested(GObject* /*object*/, +void ScreenCastPortal::OnProxyRequested(GObject* gobject, GAsyncResult* result, gpointer user_data) { - ScreenCastPortal* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); - if (!proxy) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " - << error->message; - that->PortalFailed(RequestResponse::kError); - return; - } - that->proxy_ = proxy; - that->connection_ = g_dbus_proxy_get_connection(that->proxy_); - - RTC_LOG(LS_INFO) << "Created proxy for the screen cast portal."; - - that->SessionRequest(); + static_cast(user_data)->RequestSessionUsingProxy(result); } -// static -std::string ScreenCastPortal::PrepareSignalHandle(GDBusConnection* connection, - const char* token) { - Scoped sender( - g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); - for (int i = 0; sender.get()[i]; ++i) { - if (sender.get()[i] == '.') { - sender.get()[i] = '_'; - } - } - - const char* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), - "/", token, /*end of varargs*/ nullptr); - - return handle; -} - -void ScreenCastPortal::SessionRequest() { - GVariantBuilder builder; - Scoped variant_string; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - variant_string = - g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "session_handle_token", - g_variant_new_string(variant_string.get())); - variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "handle_token", - g_variant_new_string(variant_string.get())); - - portal_handle_ = PrepareSignalHandle(connection_, variant_string.get()); - session_request_signal_id_ = SetupRequestResponseSignal( - portal_handle_.c_str(), OnSessionRequestResponseSignal); - - RTC_LOG(LS_INFO) << "Screen cast session requested."; - g_dbus_proxy_call(proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, - reinterpret_cast(OnSessionRequested), - this); +void ScreenCastPortal::RequestSession(GDBusProxy* proxy) { + proxy_ = proxy; + connection_ = g_dbus_proxy_get_connection(proxy_); + SetupSessionRequestHandlers( + "webrtc", OnSessionRequested, OnSessionRequestResponseSignal, connection_, + proxy_, cancellable_, portal_handle_, session_request_signal_id_, this); } // static void ScreenCastPortal::OnSessionRequested(GDBusProxy* proxy, GAsyncResult* result, gpointer user_data) { - ScreenCastPortal* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped variant( - g_dbus_proxy_call_finish(proxy, result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: " - << error->message; - that->PortalFailed(RequestResponse::kError); - return; - } - RTC_LOG(LS_INFO) << "Initializing the screen cast session."; - - Scoped handle; - g_variant_get_child(variant.get(), 0, "o", &handle); - if (!handle) { - RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session."; - if (that->session_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(that->connection_, - that->session_request_signal_id_); - that->session_request_signal_id_ = 0; - } - that->PortalFailed(RequestResponse::kError); - return; - } - - RTC_LOG(LS_INFO) << "Subscribing to the screen cast session."; + static_cast(user_data)->OnSessionRequestResult(proxy, + result); } // static @@ -210,30 +158,9 @@ void ScreenCastPortal::OnSessionRequestResponseSignal( gpointer user_data) { ScreenCastPortal* that = static_cast(user_data); RTC_DCHECK(that); - - RTC_LOG(LS_INFO) - << "Received response for the screen cast session subscription."; - - uint32_t portal_response; - Scoped response_data; - g_variant_get(parameters, "(u@a{sv})", &portal_response, - response_data.receive()); - Scoped session_handle( - g_variant_lookup_value(response_data.get(), "session_handle", nullptr)); - that->session_handle_ = g_variant_dup_string(session_handle.get(), nullptr); - - if (that->session_handle_.empty() || portal_response) { - RTC_LOG(LS_ERROR) - << "Failed to request the screen cast session subscription."; - that->PortalFailed(RequestResponse::kError); - return; - } - - that->session_closed_signal_id_ = g_dbus_connection_signal_subscribe( - that->connection_, kDesktopBusName, kSessionInterfaceName, "Closed", - that->session_handle_.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE, - OnSessionClosedSignal, that, /*user_data_free_func=*/nullptr); - + that->RegisterSessionClosedSignalHandler( + OnSessionClosedSignal, parameters, that->connection_, + that->session_handle_, that->session_closed_signal_id_); that->SourcesRequest(); } @@ -289,9 +216,10 @@ void ScreenCastPortal::SourcesRequest() { g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(variant_string.get())); - sources_handle_ = PrepareSignalHandle(connection_, variant_string.get()); + sources_handle_ = PrepareSignalHandle(variant_string.get(), connection_); sources_request_signal_id_ = SetupRequestResponseSignal( - sources_handle_.c_str(), OnSourcesRequestResponseSignal); + sources_handle_.c_str(), sources_request_response_signal_handler_, + user_data_, connection_); RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session."; g_dbus_proxy_call( @@ -315,7 +243,7 @@ void ScreenCastPortal::OnSourcesRequested(GDBusProxy* proxy, if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message; - that->PortalFailed(RequestResponse::kError); + that->OnPortalDone(RequestResponse::kError); return; } @@ -330,7 +258,7 @@ void ScreenCastPortal::OnSourcesRequested(GDBusProxy* proxy, that->sources_request_signal_id_); that->sources_request_signal_id_ = 0; } - that->PortalFailed(RequestResponse::kError); + that->OnPortalDone(RequestResponse::kError); return; } @@ -356,7 +284,7 @@ void ScreenCastPortal::OnSourcesRequestResponseSignal( if (portal_response) { RTC_LOG(LS_ERROR) << "Failed to select sources for the screen cast session."; - that->PortalFailed(RequestResponse::kError); + that->OnPortalDone(RequestResponse::kError); return; } @@ -364,66 +292,17 @@ void ScreenCastPortal::OnSourcesRequestResponseSignal( } void ScreenCastPortal::StartRequest() { - GVariantBuilder builder; - Scoped variant_string; - - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); - g_variant_builder_add(&builder, "{sv}", "handle_token", - g_variant_new_string(variant_string.get())); - - start_handle_ = PrepareSignalHandle(connection_, variant_string.get()); - start_request_signal_id_ = SetupRequestResponseSignal( - start_handle_.c_str(), OnStartRequestResponseSignal); - - // "Identifier for the application window", this is Wayland, so not "x11:...". - const char parent_window[] = ""; - - RTC_LOG(LS_INFO) << "Starting the screen cast session."; - g_dbus_proxy_call(proxy_, "Start", - g_variant_new("(osa{sv})", session_handle_.c_str(), - parent_window, &builder), - G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_, - reinterpret_cast(OnStartRequested), - this); + StartSessionRequest("webrtc", session_handle_, OnStartRequestResponseSignal, + OnStartRequested, proxy_, connection_, cancellable_, + start_request_signal_id_, start_handle_, this); } // static void ScreenCastPortal::OnStartRequested(GDBusProxy* proxy, GAsyncResult* result, gpointer user_data) { - ScreenCastPortal* that = static_cast(user_data); - RTC_DCHECK(that); - - Scoped error; - Scoped variant( - g_dbus_proxy_call_finish(proxy, result, error.receive())); - if (!variant) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: " - << error->message; - that->PortalFailed(RequestResponse::kError); - return; - } - - RTC_LOG(LS_INFO) << "Initializing the start of the screen cast session."; - - Scoped handle; - g_variant_get_child(variant.get(), 0, "o", handle.receive()); - if (!handle) { - RTC_LOG(LS_ERROR) - << "Failed to initialize the start of the screen cast session."; - if (that->start_request_signal_id_) { - g_dbus_connection_signal_unsubscribe(that->connection_, - that->start_request_signal_id_); - that->start_request_signal_id_ = 0; - } - that->PortalFailed(RequestResponse::kError); - return; - } - - RTC_LOG(LS_INFO) << "Subscribed to the start signal."; + static_cast(user_data)->OnStartRequestResult(proxy, + result); } // static @@ -445,7 +324,7 @@ void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection, response_data.receive()); if (portal_response || !response_data) { RTC_LOG(LS_ERROR) << "Failed to start the screen cast session."; - that->PortalFailed(static_cast(portal_response)); + that->OnPortalDone(static_cast(portal_response)); return; } @@ -478,6 +357,10 @@ void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection, that->OpenPipeWireRemote(); } +uint32_t ScreenCastPortal::pipewire_stream_node_id() { + return pw_stream_node_id_; +} + void ScreenCastPortal::OpenPipeWireRemote() { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); @@ -508,7 +391,7 @@ void ScreenCastPortal::OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, return; RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: " << error->message; - that->PortalFailed(RequestResponse::kError); + that->OnPortalDone(RequestResponse::kError); return; } @@ -520,13 +403,11 @@ void ScreenCastPortal::OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, if (that->pw_fd_ == -1) { RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " << error->message; - that->PortalFailed(RequestResponse::kError); + that->OnPortalDone(RequestResponse::kError); return; } - that->notifier_->OnScreenCastRequestResult( - ScreenCastPortal::RequestResponse::kSuccess, that->pw_stream_node_id_, - that->pw_fd_); + that->OnPortalDone(RequestResponse::kSuccess); } } // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.h b/modules/desktop_capture/linux/wayland/screencast_portal.h index fd6bb1d468..73a6038c3c 100644 --- a/modules/desktop_capture/linux/wayland/screencast_portal.h +++ b/modules/desktop_capture/linux/wayland/screencast_portal.h @@ -15,12 +15,28 @@ #include -#include "absl/types/optional.h" +#include "modules/desktop_capture/linux/wayland/portal_request_response.h" +#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h" +#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" +#include "modules/desktop_capture/linux/wayland/xdg_session_details.h" namespace webrtc { -class ScreenCastPortal { +class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface { public: + using ProxyRequestResponseHandler = void (*)(GObject* object, + GAsyncResult* result, + gpointer user_data); + + using SourcesRequestResponseSignalHandler = + void (*)(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + // Values are set based on source type property in // xdg-desktop-portal/screencast // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml @@ -43,20 +59,9 @@ class ScreenCastPortal { }; // Interface that must be implemented by the ScreenCastPortal consumers. - enum class RequestResponse { - // Success, the request is carried out. - kSuccess, - // The user cancelled the interaction. - kUserCancelled, - // The user interaction was ended in some other way. - kError, - - kMaxValue = kError - }; - class PortalNotifier { public: - virtual void OnScreenCastRequestResult(RequestResponse result, + virtual void OnScreenCastRequestResult(xdg_portal::RequestResponse result, uint32_t stream_node_id, int fd) = 0; virtual void OnScreenCastSessionClosed() = 0; @@ -66,8 +71,15 @@ class ScreenCastPortal { virtual ~PortalNotifier() = default; }; - explicit ScreenCastPortal(CaptureSourceType source_type, + explicit ScreenCastPortal(ScreenCastPortal::CaptureSourceType source_type, PortalNotifier* notifier); + explicit ScreenCastPortal( + CaptureSourceType source_type, + PortalNotifier* notifier, + ProxyRequestResponseHandler proxy_request_response_handler, + SourcesRequestResponseSignalHandler + sources_request_response_signal_handler, + gpointer user_data); ~ScreenCastPortal(); // Initialize ScreenCastPortal with series of DBus calls where we try to @@ -77,7 +89,22 @@ class ScreenCastPortal { // The observer will return whether the communication with xdg-desktop-portal // was successful and only then you will be able to get all the required // information in order to continue working with PipeWire. - void Start(); + void Start() override; + xdg_portal::SessionDetails GetSessionDetails() override; + + // Method to notify the reason for failure of a portal request. + void OnPortalDone(xdg_portal::RequestResponse result) override; + + // Sends a create session request to the portal. + void RequestSession(GDBusProxy* proxy) override; + void Cleanup(); + + // Set of methods leveraged by remote desktop portal to setup a common session + // with screen cast portal. + void SetSessionDetails(const xdg_portal::SessionDetails& session_details); + uint32_t pipewire_stream_node_id(); + void SourcesRequest(); + void OpenPipeWireRemote(); private: PortalNotifier* notifier_; @@ -92,6 +119,10 @@ class ScreenCastPortal { CursorMode cursor_mode_ = ScreenCastPortal::CursorMode::kMetadata; + ProxyRequestResponseHandler proxy_request_response_handler_; + SourcesRequestResponseSignalHandler sources_request_response_signal_handler_; + gpointer user_data_; + GDBusConnection* connection_ = nullptr; GDBusProxy* proxy_ = nullptr; GCancellable* cancellable_ = nullptr; @@ -104,19 +135,10 @@ class ScreenCastPortal { guint start_request_signal_id_ = 0; guint session_closed_signal_id_ = 0; - void PortalFailed(RequestResponse result); - - uint32_t SetupRequestResponseSignal(const char* object_path, - GDBusSignalCallback callback); - + void UnsubscribeSignalHandlers(); static void OnProxyRequested(GObject* object, GAsyncResult* result, gpointer user_data); - - static std::string PrepareSignalHandle(GDBusConnection* connection, - const char* token); - - void SessionRequest(); static void OnSessionRequested(GDBusProxy* proxy, GAsyncResult* result, gpointer user_data); @@ -134,7 +156,6 @@ class ScreenCastPortal { const char* signal_name, GVariant* parameters, gpointer user_data); - void SourcesRequest(); static void OnSourcesRequested(GDBusProxy* proxy, GAsyncResult* result, gpointer user_data); @@ -158,7 +179,6 @@ class ScreenCastPortal { GVariant* parameters, gpointer user_data); - void OpenPipeWireRemote(); static void OnOpenPipeWireRemoteRequested(GDBusProxy* proxy, GAsyncResult* result, gpointer user_data); diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc new file mode 100644 index 0000000000..6d0df404f9 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc @@ -0,0 +1,133 @@ +/* + * Copyright 2022 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/desktop_capture/linux/wayland/screencast_stream_utils.h" + +#include +#include +#include + +#include + +#include "rtc_base/string_to_number.h" + +#if !PW_CHECK_VERSION(0, 3, 29) +#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) +#endif +#if !PW_CHECK_VERSION(0, 3, 33) +#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) +#endif + +namespace webrtc { + +PipeWireThreadLoopLock::PipeWireThreadLoopLock(pw_thread_loop* loop) + : loop_(loop) { + pw_thread_loop_lock(loop_); +} + +PipeWireThreadLoopLock::~PipeWireThreadLoopLock() { + pw_thread_loop_unlock(loop_); +} + +PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) { + std::vector parsed_version; + rtc::split(version, '.', &parsed_version); + + if (parsed_version.size() != 3) { + return {}; + } + + absl::optional major = rtc::StringToNumber(parsed_version.at(0)); + absl::optional minor = rtc::StringToNumber(parsed_version.at(1)); + absl::optional micro = rtc::StringToNumber(parsed_version.at(2)); + + // Return invalid version if we failed to parse it + if (!major || !minor || !micro) { + return {}; + } + + return {major.value(), minor.value(), micro.value()}; +} + +bool PipeWireVersion::operator>=(const PipeWireVersion& other) { + if (!major && !minor && !micro) { + return false; + } + + return std::tie(major, minor, micro) >= + std::tie(other.major, other.minor, other.micro); +} + +bool PipeWireVersion::operator<=(const PipeWireVersion& other) { + if (!major && !minor && !micro) { + return false; + } + + return std::tie(major, minor, micro) <= + std::tie(other.major, other.minor, other.micro); +} + +spa_pod* BuildFormat(spa_pod_builder* builder, + uint32_t format, + const std::vector& modifiers, + const struct spa_rectangle* resolution) { + spa_pod_frame frames[2]; + spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; + spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; + + spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, + SPA_PARAM_EnumFormat); + spa_pod_builder_add(builder, SPA_FORMAT_mediaType, + SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); + spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, + SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); + + if (modifiers.size()) { + if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(builder, modifiers[0]); + } else { + spa_pod_builder_prop( + builder, SPA_FORMAT_VIDEO_modifier, + SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); + + // modifiers from the array + bool first = true; + for (int64_t val : modifiers) { + spa_pod_builder_long(builder, val); + // Add the first modifier twice as the very first value is the default + // option + if (first) { + spa_pod_builder_long(builder, val); + first = false; + } + } + spa_pod_builder_pop(builder, &frames[1]); + } + } + + if (resolution) { + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, + SPA_POD_Rectangle(resolution), 0); + } else { + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, + SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, + &pw_min_screen_bounds, + &pw_max_screen_bounds), + 0); + } + + return static_cast(spa_pod_builder_pop(builder, &frames[0])); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h new file mode 100644 index 0000000000..70262c2e39 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/screencast_stream_utils.h @@ -0,0 +1,62 @@ +/* + * Copyright 2022 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_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ + +#include + +#include +#include + +#include "rtc_base/string_encode.h" + +struct pw_thread_loop; +struct spa_pod; +struct spa_pod_builder; +struct spa_rectangle; + +namespace webrtc { + +// Locks pw_thread_loop in the current scope +class PipeWireThreadLoopLock { + public: + explicit PipeWireThreadLoopLock(pw_thread_loop* loop); + ~PipeWireThreadLoopLock(); + + private: + pw_thread_loop* const loop_; +}; + +struct PipeWireVersion { + static PipeWireVersion Parse(const absl::string_view& version); + + // Returns whether current version is newer or same as required version + bool operator>=(const PipeWireVersion& other); + // Returns whether current version is older or same as required version + bool operator<=(const PipeWireVersion& other); + + int major = 0; + int minor = 0; + int micro = 0; +}; + +// Returns a spa_pod used to build PipeWire stream format using given +// arguments. Modifiers are optional value and when present they will be +// used with SPA_POD_PROP_FLAG_MANDATORY and SPA_POD_PROP_FLAG_DONT_FIXATE +// flags. +spa_pod* BuildFormat(spa_pod_builder* builder, + uint32_t format, + const std::vector& modifiers, + const struct spa_rectangle* resolution); + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_ diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc index a8c86e267b..a60b6abcdf 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc @@ -12,25 +12,18 @@ #include #include -#include -#include #include -#include #include -#include -#include -#include #include #include "absl/memory/memory.h" #include "modules/desktop_capture/linux/wayland/egl_dmabuf.h" +#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h" #include "modules/desktop_capture/screen_capture_frame_queue.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/sanitizer.h" -#include "rtc_base/string_encode.h" -#include "rtc_base/string_to_number.h" #include "rtc_base/synchronization/mutex.h" #if defined(WEBRTC_DLOPEN_PIPEWIRE) @@ -50,110 +43,16 @@ const char kPipeWireLib[] = "libpipewire-0.3.so.0"; const char kDrmLib[] = "libdrm.so.2"; #endif -#if !PW_CHECK_VERSION(0, 3, 29) -#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3) -#endif -#if !PW_CHECK_VERSION(0, 3, 33) -#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4) -#endif - constexpr int kCursorBpp = 4; constexpr int CursorMetaSize(int w, int h) { return (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * kCursorBpp); } -struct PipeWireVersion { - int major = 0; - int minor = 0; - int micro = 0; -}; - constexpr PipeWireVersion kDmaBufMinVersion = {0, 3, 24}; constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33}; constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40}; -PipeWireVersion ParsePipeWireVersion(const char* version) { - std::vector parsed_version; - rtc::split(version, '.', &parsed_version); - - if (parsed_version.size() != 3) { - return {}; - } - - absl::optional major = rtc::StringToNumber(parsed_version.at(0)); - absl::optional minor = rtc::StringToNumber(parsed_version.at(1)); - absl::optional micro = rtc::StringToNumber(parsed_version.at(2)); - - // Return invalid version if we failed to parse it - if (!major || !minor || !micro) { - return {0, 0, 0}; - } - - return {major.value(), micro.value(), micro.value()}; -} - -spa_pod* BuildFormat(spa_pod_builder* builder, - uint32_t format, - const std::vector& modifiers) { - bool first = true; - spa_pod_frame frames[2]; - spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1}; - spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX}; - - spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format, - SPA_PARAM_EnumFormat); - spa_pod_builder_add(builder, SPA_FORMAT_mediaType, - SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); - spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, - SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); - spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - - if (modifiers.size()) { - if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) { - spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY); - spa_pod_builder_long(builder, modifiers[0]); - } else { - spa_pod_builder_prop( - builder, SPA_FORMAT_VIDEO_modifier, - SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0); - - // modifiers from the array - for (int64_t val : modifiers) { - spa_pod_builder_long(builder, val); - // Add the first modifier twice as the very first value is the default - // option - if (first) { - spa_pod_builder_long(builder, val); - first = false; - } - } - spa_pod_builder_pop(builder, &frames[1]); - } - } - - spa_pod_builder_add( - builder, SPA_FORMAT_VIDEO_size, - SPA_POD_CHOICE_RANGE_Rectangle( - &pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), - 0); - - return static_cast(spa_pod_builder_pop(builder, &frames[0])); -} - -class PipeWireThreadLoopLock { - public: - explicit PipeWireThreadLoopLock(pw_thread_loop* loop) : loop_(loop) { - pw_thread_loop_lock(loop_); - } - ~PipeWireThreadLoopLock() { pw_thread_loop_unlock(loop_); } - - private: - pw_thread_loop* const loop_; -}; - class ScopedBuf { public: ScopedBuf() {} @@ -260,32 +159,6 @@ class SharedScreenCastStreamPrivate { static void OnRenegotiateFormat(void* data, uint64_t); }; -bool operator>=(const PipeWireVersion& current_pw_version, - const PipeWireVersion& required_pw_version) { - if (!current_pw_version.major && !current_pw_version.minor && - !current_pw_version.micro) { - return false; - } - - return std::tie(current_pw_version.major, current_pw_version.minor, - current_pw_version.micro) >= - std::tie(required_pw_version.major, required_pw_version.minor, - required_pw_version.micro); -} - -bool operator<=(const PipeWireVersion& current_pw_version, - const PipeWireVersion& required_pw_version) { - if (!current_pw_version.major && !current_pw_version.minor && - !current_pw_version.micro) { - return false; - } - - return std::tie(current_pw_version.major, current_pw_version.minor, - current_pw_version.micro) <= - std::tie(required_pw_version.major, required_pw_version.minor, - required_pw_version.micro); -} - void SharedScreenCastStreamPrivate::OnCoreError(void* data, uint32_t id, int seq, @@ -304,7 +177,7 @@ void SharedScreenCastStreamPrivate::OnCoreInfo(void* data, static_cast(data); RTC_DCHECK(stream); - stream->pw_server_version_ = ParsePipeWireVersion(info->version); + stream->pw_server_version_ = PipeWireVersion::Parse(info->version); } void SharedScreenCastStreamPrivate::OnCoreDone(void* data, @@ -457,9 +330,11 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) { for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) { if (!that->modifiers_.empty()) { - params.push_back(BuildFormat(&builder, format, that->modifiers_)); + params.push_back(BuildFormat(&builder, format, that->modifiers_, + /*resolution=*/nullptr)); } - params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}, + /*resolution=*/nullptr)); } pw_stream_update_params(that->pw_stream_, params.data(), params.size()); @@ -530,7 +405,7 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( return false; } - pw_client_version_ = ParsePipeWireVersion(pw_get_library_version()); + pw_client_version_ = PipeWireVersion::Parse(pw_get_library_version()); // Initialize event handlers, remote end and stream-related. pw_core_events_.version = PW_VERSION_CORE_EVENTS; @@ -546,7 +421,12 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( { PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_); - pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); + if (!pw_fd_) { + pw_core_ = pw_context_connect(pw_context_, nullptr, 0); + } else { + pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0); + } + if (!pw_core_) { RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context"; return false; @@ -590,11 +470,13 @@ bool SharedScreenCastStreamPrivate::StartScreenCastStream( modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format); if (!modifiers_.empty()) { - params.push_back(BuildFormat(&builder, format, modifiers_)); + params.push_back(BuildFormat(&builder, format, modifiers_, + /*resolution=*/nullptr)); } } - params.push_back(BuildFormat(&builder, format, /*modifiers=*/{})); + params.push_back(BuildFormat(&builder, format, /*modifiers=*/{}, + /*resolution=*/nullptr)); } if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_, @@ -638,6 +520,7 @@ DesktopVector SharedScreenCastStreamPrivate::CaptureCursorPosition() { return mouse_cursor_position_; } +RTC_NO_SANITIZE("cfi-icall") void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { spa_buffer* spa_buffer = buffer->buffer; ScopedBuf map; @@ -650,7 +533,7 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { const struct spa_meta_cursor* cursor = static_cast(spa_buffer_find_meta_data( spa_buffer, SPA_META_Cursor, sizeof(*cursor))); - if (spa_meta_cursor_is_valid(cursor)) { + if (cursor && spa_meta_cursor_is_valid(cursor)) { struct spa_meta_bitmap* bitmap = nullptr; if (cursor->bitmap_offset) @@ -735,6 +618,7 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { if (!src) { return; } + struct spa_meta_region* video_metadata = static_cast(spa_buffer_find_meta_data( spa_buffer, SPA_META_VideoCrop, sizeof(*video_metadata))); @@ -819,6 +703,9 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) { tmp_src += queue_.current_frame()->stride(); } } + + queue_.current_frame()->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(queue_.current_frame()->size())); } void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame, @@ -842,6 +729,10 @@ SharedScreenCastStream::CreateDefault() { return rtc::scoped_refptr(new SharedScreenCastStream()); } +bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id) { + return private_->StartScreenCastStream(stream_node_id, 0); +} + bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id, int fd) { return private_->StartScreenCastStream(stream_node_id, fd); diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h index 443ec745d5..1e9fbe5f70 100644 --- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.h +++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.h @@ -29,6 +29,7 @@ class RTC_EXPORT SharedScreenCastStream public: static rtc::scoped_refptr CreateDefault(); + bool StartScreenCastStream(uint32_t stream_node_id); bool StartScreenCastStream(uint32_t stream_node_id, int fd); void StopScreenCastStream(); diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc new file mode 100644 index 0000000000..4fdf54abce --- /dev/null +++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc @@ -0,0 +1,171 @@ +/* + * Copyright 2022 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/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h" + +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +std::string RequestResponseToString(RequestResponse request) { + switch (request) { + case RequestResponse::kUnknown: + return "kUnknown"; + case RequestResponse::kSuccess: + return "kSuccess"; + case RequestResponse::kUserCancelled: + return "kUserCancelled"; + case RequestResponse::kError: + return "kError"; + default: + return "Uknown"; + } +} + +std::string PrepareSignalHandle(const char* token, + GDBusConnection* connection) { + Scoped sender( + g_strdup(g_dbus_connection_get_unique_name(connection) + 1)); + for (int i = 0; sender.get()[i]; ++i) { + if (sender.get()[i] == '.') { + sender.get()[i] = '_'; + } + } + const char* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), + "/", token, /*end of varargs*/ nullptr); + return handle; +} + +uint32_t SetupRequestResponseSignal(const char* object_path, + const GDBusSignalCallback callback, + gpointer user_data, + GDBusConnection* connection) { + return g_dbus_connection_signal_subscribe( + connection, kDesktopBusName, kRequestInterfaceName, "Response", + object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + callback, user_data, /*user_data_free_func=*/nullptr); +} + +void RequestSessionProxy(const char* interface_name, + const ProxyRequestCallback proxy_request_callback, + GCancellable* cancellable, + gpointer user_data) { + g_dbus_proxy_new_for_bus( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, + kDesktopBusName, kDesktopObjectPath, interface_name, cancellable, + reinterpret_cast(proxy_request_callback), user_data); +} + +void SetupSessionRequestHandlers( + const std::string& portal_prefix, + const SessionRequestCallback session_request_callback, + const SessionRequestResponseSignalHandler request_response_signale_handler, + GDBusConnection* connection, + GDBusProxy* proxy, + GCancellable* cancellable, + std::string& portal_handle, + guint& session_request_signal_id, + gpointer user_data) { + GVariantBuilder builder; + Scoped variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = g_strdup_printf("%s_session%d", portal_prefix.c_str(), + g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "session_handle_token", + g_variant_new_string(variant_string.get())); + + variant_string = g_strdup_printf("%s_%d", portal_prefix.c_str(), + g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + portal_handle = PrepareSignalHandle(variant_string.get(), connection); + session_request_signal_id = SetupRequestResponseSignal( + portal_handle.c_str(), request_response_signale_handler, user_data, + connection); + + RTC_LOG(LS_INFO) << "Desktop session requested."; + g_dbus_proxy_call( + proxy, "CreateSession", g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, + reinterpret_cast(session_request_callback), + user_data); +} + +void StartSessionRequest( + const std::string& prefix, + const std::string session_handle, + const StartRequestResponseSignalHandler signal_handler, + const SessionStartRequestedHandler session_started_handler, + GDBusProxy* proxy, + GDBusConnection* connection, + GCancellable* cancellable, + guint& start_request_signal_id, + std::string& start_handle, + gpointer user_data) { + GVariantBuilder builder; + Scoped variant_string; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = + g_strdup_printf("%s%d", prefix.c_str(), g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + start_handle = PrepareSignalHandle(variant_string.get(), connection); + start_request_signal_id = SetupRequestResponseSignal( + start_handle.c_str(), signal_handler, user_data, connection); + + // "Identifier for the application window", this is Wayland, so not "x11:...". + const char parent_window[] = ""; + + RTC_LOG(LS_INFO) << "Starting the portal session."; + g_dbus_proxy_call( + proxy, "Start", + g_variant_new("(osa{sv})", session_handle.c_str(), parent_window, + &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable, + reinterpret_cast(session_started_handler), + user_data); +} + +void TearDownSession(std::string session_handle, + GDBusProxy* proxy, + GCancellable* cancellable, + GDBusConnection* connection) { + if (!session_handle.empty()) { + Scoped message( + g_dbus_message_new_method_call(kDesktopBusName, session_handle.c_str(), + kSessionInterfaceName, "Close")); + if (message.get()) { + Scoped error; + g_dbus_connection_send_message(connection, message.get(), + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + /*out_serial=*/nullptr, error.receive()); + if (error.get()) { + RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message; + } + } + } + + if (cancellable) { + g_cancellable_cancel(cancellable); + g_object_unref(cancellable); + } + + if (proxy) { + g_object_unref(proxy); + } +} + +} // namespace xdg_portal +} // namespace webrtc diff --git a/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h new file mode 100644 index 0000000000..7f1ef0b8d5 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h @@ -0,0 +1,108 @@ +/* + * Copyright 2022 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_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ + +#include +#include + +#include +#include + +#include "modules/desktop_capture/linux/wayland/portal_request_response.h" +#include "modules/desktop_capture/linux/wayland/scoped_glib.h" +#include "modules/desktop_capture/linux/wayland/xdg_session_details.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace xdg_portal { + +constexpr char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; +constexpr char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; +constexpr char kDesktopRequestObjectPath[] = + "/org/freedesktop/portal/desktop/request"; +constexpr char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; +constexpr char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; +constexpr char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; + +using ProxyRequestCallback = void (*)(GObject*, GAsyncResult*, gpointer); +using SessionRequestCallback = void (*)(GDBusProxy*, GAsyncResult*, gpointer); +using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); +using SessionRequestResponseSignalCallback = void (*)(std::string); +using StartRequestResponseSignalHandler = void (*)(GDBusConnection*, + const char*, + const char*, + const char*, + const char*, + GVariant*, + gpointer); +using SessionStartRequestedHandler = void (*)(GDBusProxy*, + GAsyncResult*, + gpointer); + +std::string RequestResponseToString(RequestResponse request); + +// Returns a string path for signal handle based on the provided connection and +// token. +std::string PrepareSignalHandle(const char* token, GDBusConnection* connection); + +// Sets up the callback to execute when a response signal is received for the +// given object. +uint32_t SetupRequestResponseSignal(const char* object_path, + const GDBusSignalCallback callback, + gpointer user_data, + GDBusConnection* connection); + +void RequestSessionProxy(const char* interface_name, + const ProxyRequestCallback proxy_request_callback, + GCancellable* cancellable, + gpointer user_data); + +void SetupSessionRequestHandlers( + const std::string& portal_prefix, + const SessionRequestCallback session_request_callback, + const SessionRequestResponseSignalHandler request_response_signale_handler, + GDBusConnection* connection, + GDBusProxy* proxy, + GCancellable* cancellable, + std::string& portal_handle, + guint& session_request_signal_id, + gpointer user_data); + +void StartSessionRequest( + const std::string& prefix, + const std::string session_handle, + const StartRequestResponseSignalHandler signal_handler, + const SessionStartRequestedHandler session_started_handler, + GDBusProxy* proxy, + GDBusConnection* connection, + GCancellable* cancellable, + guint& start_request_signal_id, + std::string& start_handle, + gpointer user_data); + +// Tears down the portal session and cleans up related objects. +void TearDownSession(std::string session_handle, + GDBusProxy* proxy, + GCancellable* cancellable, + GDBusConnection* connection); + +} // namespace xdg_portal +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_ diff --git a/modules/desktop_capture/linux/wayland/xdg_session_details.h b/modules/desktop_capture/linux/wayland/xdg_session_details.h new file mode 100644 index 0000000000..b70ac4aa59 --- /dev/null +++ b/modules/desktop_capture/linux/wayland/xdg_session_details.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 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_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_ +#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_ + +#include + +#include + +namespace webrtc { +namespace xdg_portal { + +// Details of the session associated with XDG desktop portal session. Portal API +// calls can be invoked by utilizing the information here. +struct SessionDetails { + GDBusProxy* proxy = nullptr; + GCancellable* cancellable = nullptr; + std::string session_handle; + uint32_t pipewire_stream_node_id = 0; +}; + +} // namespace xdg_portal +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_ diff --git a/modules/desktop_capture/linux/x11/screen_capturer_x11.cc b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc index 2de387578e..3cc256bdbd 100644 --- a/modules/desktop_capture/linux/x11/screen_capturer_x11.cc +++ b/modules/desktop_capture/linux/x11/screen_capturer_x11.cc @@ -350,10 +350,10 @@ bool ScreenCapturerX11::HandleXEvent(const XEvent& event) { XRRUpdateConfiguration(const_cast(&event)); UpdateMonitors(); RTC_LOG(LS_INFO) << "XRandR screen change event received."; - return true; + return false; } else if (event.type == ConfigureNotify) { ScreenConfigurationChanged(); - return true; + return false; } return false; } diff --git a/modules/desktop_capture/mac/desktop_frame_cgimage.mm b/modules/desktop_capture/mac/desktop_frame_cgimage.mm index fb13fe2738..0fb69b272d 100644 --- a/modules/desktop_capture/mac/desktop_frame_cgimage.mm +++ b/modules/desktop_capture/mac/desktop_frame_cgimage.mm @@ -10,6 +10,8 @@ #include "modules/desktop_capture/mac/desktop_frame_cgimage.h" +#include + #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -73,7 +75,11 @@ CGColorSpaceRef cg_color_space = CGImageGetColorSpace(cg_image.get()); if (cg_color_space) { +#if !defined(MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13 rtc::ScopedCFTypeRef cf_icc_profile(CGColorSpaceCopyICCProfile(cg_color_space)); +#else + rtc::ScopedCFTypeRef cf_icc_profile(CGColorSpaceCopyICCData(cg_color_space)); +#endif if (cf_icc_profile) { const uint8_t* data_as_byte = reinterpret_cast(CFDataGetBytePtr(cf_icc_profile.get())); diff --git a/modules/desktop_capture/mac/window_list_utils.cc b/modules/desktop_capture/mac/window_list_utils.cc index d2fb20ed4c..5d881662ea 100644 --- a/modules/desktop_capture/mac/window_list_utils.cc +++ b/modules/desktop_capture/mac/window_list_utils.cc @@ -31,6 +31,11 @@ namespace webrtc { namespace { +// WindowName of the status indicator dot shown since Monterey in the taskbar. +// Testing on 12.2.1 shows this is independent of system language setting. +const CFStringRef kStatusIndicator = CFSTR("StatusIndicator"); +const CFStringRef kStatusIndicatorOwnerName = CFSTR("Window Server"); + bool ToUtf8(const CFStringRef str16, std::string* str8) { size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16), kCFStringEncodingUTF8) + @@ -145,6 +150,17 @@ bool GetWindowList(rtc::FunctionView on_window, continue; } + CFStringRef window_owner_name = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerName)); + // Ignore the red dot status indicator shown in the stats bar. Unlike the + // rest of the system UI it has a window_layer of 0, so was otherwise + // included. See crbug.com/1297731. + if (window_title && CFEqual(window_title, kStatusIndicator) && + window_owner_name && + CFEqual(window_owner_name, kStatusIndicatorOwnerName)) { + continue; + } + if (!on_window(window)) { break; } diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/modules/desktop_capture/win/dxgi_duplicator_controller.cc index 59ad4ebda4..a776896f6c 100644 --- a/modules/desktop_capture/win/dxgi_duplicator_controller.cc +++ b/modules/desktop_capture/win/dxgi_duplicator_controller.cc @@ -25,6 +25,26 @@ namespace webrtc { +namespace { + +constexpr DWORD kInvalidSessionId = 0xFFFFFFFF; + +DWORD GetCurrentSessionId() { + DWORD session_id = kInvalidSessionId; + if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) { + RTC_LOG(LS_WARNING) + << "Failed to retrieve current session Id, current binary " + "may not have required priviledge."; + } + return session_id; +} + +bool IsConsoleSession() { + return WTSGetActiveConsoleSessionId() == GetCurrentSessionId(); +} + +} // namespace + // static std::string DxgiDuplicatorController::ResultName( DxgiDuplicatorController::Result result) { @@ -57,14 +77,8 @@ DxgiDuplicatorController::Instance() { // static bool DxgiDuplicatorController::IsCurrentSessionSupported() { - DWORD session_id = 0; - if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) { - RTC_LOG(LS_WARNING) - << "Failed to retrieve current session Id, current binary " - "may not have required priviledge."; - return false; - } - return session_id != 0; + DWORD current_session_id = GetCurrentSessionId(); + return current_session_id != kInvalidSessionId && current_session_id != 0; } DxgiDuplicatorController::DxgiDuplicatorController() : refcount_(0) {} @@ -423,15 +437,28 @@ bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, // On a modern system, the FPS / monitor refresh rate is usually larger than // or equal to 60. So 17 milliseconds is enough to capture at least one frame. const int64_t ms_per_frame = 17; - // Skips the first frame to ensure a full frame refresh has happened before - // this function returns. - const int64_t frames_to_skip = 1; + // Skip frames to ensure a full frame refresh has occurred and the DXGI + // machinery is producing frames before this function returns. + int64_t frames_to_skip = 1; // The total time out milliseconds for this function. If we cannot get enough // frames during this time interval, this function returns false, and cause // the DXGI components to be reinitialized. This usually should not happen // unless the system is switching display mode when this function is being // called. 500 milliseconds should be enough for ~30 frames. const int64_t timeout_ms = 500; + + if (GetNumFramesCaptured() == 0 && !IsConsoleSession()) { + // When capturing a console session, waiting for a single frame is + // sufficient to ensure that DXGI output duplication is working. When the + // session is not attached to the console, it has been observed that DXGI + // may produce up to 4 frames (typically 1-2 though) before stopping. When + // this condition occurs, no errors are returned from the output duplication + // API, it simply appears that nothing is changing on the screen. Thus for + // detached sessions, we need to capture a few extra frames before we can be + // confident that output duplication was initialized properly. + frames_to_skip = 5; + } + if (GetNumFramesCaptured() >= frames_to_skip) { return true; } @@ -450,17 +477,16 @@ bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, } const int64_t start_ms = rtc::TimeMillis(); - int64_t last_frame_start_ms = 0; while (GetNumFramesCaptured() < frames_to_skip) { - if (GetNumFramesCaptured() > 0) { - // Sleep `ms_per_frame` before capturing next frame to ensure the screen - // has been updated by the video adapter. - webrtc::SleepMs(ms_per_frame - (rtc::TimeMillis() - last_frame_start_ms)); - } - last_frame_start_ms = rtc::TimeMillis(); if (!DoDuplicateAll(context, shared_frame)) { return false; } + + // Calling DoDuplicateAll() may change the number of frames captured. + if (GetNumFramesCaptured() >= frames_to_skip) { + break; + } + if (rtc::TimeMillis() - start_ms > timeout_ms) { RTC_LOG(LS_ERROR) << "Failed to capture " << frames_to_skip << " frames " @@ -468,6 +494,10 @@ bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, << timeout_ms << " milliseconds."; return false; } + + // Sleep `ms_per_frame` before attempting to capture the next frame to + // ensure the video adapter has time to update the screen. + webrtc::SleepMs(ms_per_frame); } return true; } diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.h b/modules/desktop_capture/win/dxgi_duplicator_controller.h index 43c1a79874..88c2939187 100644 --- a/modules/desktop_capture/win/dxgi_duplicator_controller.h +++ b/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -41,6 +41,10 @@ namespace webrtc { // but a later Duplicate() returns false, this usually means the display mode is // changing. Consumers should retry after a while. (Typically 50 milliseconds, // but according to hardware performance, this time may vary.) +// The underyling DxgiOutputDuplicators may take an additional reference on the +// frame passed in to the Duplicate methods so that they can guarantee delivery +// of new frames when requested; since if there have been no updates to the +// surface, they may be unable to capture a frame. class RTC_EXPORT DxgiDuplicatorController { public: using Context = DxgiFrameContext; @@ -89,7 +93,8 @@ class RTC_EXPORT DxgiDuplicatorController { // function returns false, the information in `info` may not accurate. bool RetrieveD3dInfo(D3dInfo* info); - // Captures current screen and writes into `frame`. + // Captures current screen and writes into `frame`. May retain a reference to + // `frame`'s underlying |SharedDesktopFrame|. // TODO(zijiehe): Windows cannot guarantee the frames returned by each // IDXGIOutputDuplication are synchronized. But we are using a totally // different threading model than the way Windows suggested, it's hard to @@ -98,7 +103,8 @@ class RTC_EXPORT DxgiDuplicatorController { // Captures one monitor and writes into target. `monitor_id` should >= 0. If // `monitor_id` is greater than the total screen count of all the Duplicators, - // this function returns false. + // this function returns false. May retain a reference to `frame`'s underlying + // |SharedDesktopFrame|. Result DuplicateMonitor(DxgiFrame* frame, int monitor_id); // Returns dpi of current system. Returns an empty DesktopVector if system diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.h b/modules/desktop_capture/win/dxgi_output_duplicator.h index 198b5a636b..df15fe566e 100644 --- a/modules/desktop_capture/win/dxgi_output_duplicator.h +++ b/modules/desktop_capture/win/dxgi_output_duplicator.h @@ -61,6 +61,11 @@ class DxgiOutputDuplicator { // function copies the content to the rectangle of (offset.x(), offset.y()) to // (offset.x() + desktop_rect_.width(), offset.y() + desktop_rect_.height()). // Returns false in case of a failure. + // May retain a reference to `target` so that a "captured" frame can be + // returned in the event that a new frame is not ready to be captured yet. + // (Or in other words, if the call to IDXGIOutputDuplication::AcquireNextFrame + // indicates that there is not yet a new frame, this is usually because no + // updates have occurred to the frame). bool Duplicate(Context* context, DesktopVector offset, SharedDesktopFrame* target); diff --git a/modules/desktop_capture/win/full_screen_win_application_handler.cc b/modules/desktop_capture/win/full_screen_win_application_handler.cc index ad45047cfc..4222dfc01e 100644 --- a/modules/desktop_capture/win/full_screen_win_application_handler.cc +++ b/modules/desktop_capture/win/full_screen_win_application_handler.cc @@ -9,11 +9,14 @@ */ #include "modules/desktop_capture/win/full_screen_win_application_handler.h" + #include #include #include #include #include + +#include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "modules/desktop_capture/win/screen_capture_utils.h" #include "modules/desktop_capture/win/window_capture_utils.h" @@ -145,7 +148,8 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler { std::string GetDocumentFromEditorTitle(HWND window) const { std::string title = WindowText(window); auto position = title.find(kDocumentTitleSeparator); - return rtc::string_trim(title.substr(0, position)); + return std::string(absl::StripAsciiWhitespace( + absl::string_view(title).substr(0, position))); } std::string GetDocumentFromSlideShowTitle(HWND window) const { @@ -158,12 +162,13 @@ class FullScreenPowerPointHandler : public FullScreenApplicationHandler { if (right_pos > left_pos + kSeparatorLength) { auto result_len = right_pos - left_pos - kSeparatorLength; - auto document = title.substr(left_pos + kSeparatorLength, result_len); - return rtc::string_trim(document); + auto document = absl::string_view(title).substr( + left_pos + kSeparatorLength, result_len); + return std::string(absl::StripAsciiWhitespace(document)); } else { - auto document = - title.substr(left_pos + kSeparatorLength, std::wstring::npos); - return rtc::string_trim(document); + auto document = absl::string_view(title).substr( + left_pos + kSeparatorLength, std::wstring::npos); + return std::string(absl::StripAsciiWhitespace(document)); } } diff --git a/modules/desktop_capture/win/screen_capture_utils.cc b/modules/desktop_capture/win/screen_capture_utils.cc index 841153366c..3d4aecf14d 100644 --- a/modules/desktop_capture/win/screen_capture_utils.cc +++ b/modules/desktop_capture/win/screen_capture_utils.cc @@ -24,12 +24,16 @@ namespace webrtc { +bool HasActiveDisplay() { + DesktopCapturer::SourceList screens; + + return GetScreenList(&screens) && !screens.empty(); +} + bool GetScreenList(DesktopCapturer::SourceList* screens, std::vector* device_names /* = nullptr */) { - RTC_DCHECK_EQ(screens->size(), 0U); - if (device_names) { - RTC_DCHECK_EQ(device_names->size(), 0U); - } + RTC_DCHECK(screens->empty()); + RTC_DCHECK(!device_names || device_names->empty()); BOOL enum_result = TRUE; for (int device_index = 0;; ++device_index) { @@ -91,6 +95,12 @@ bool IsMonitorValid(const HMONITOR monitor) { // An HMONITOR of 0 refers to a virtual monitor that spans all physical // monitors. if (monitor == 0) { + // There is a bug in a Windows OS API that causes a crash when capturing if + // there are no active displays. We must ensure there is an active display + // before returning true. + if (!HasActiveDisplay()) + return false; + return true; } diff --git a/modules/desktop_capture/win/screen_capture_utils.h b/modules/desktop_capture/win/screen_capture_utils.h index bcb183b9d2..97bfe816d8 100644 --- a/modules/desktop_capture/win/screen_capture_utils.h +++ b/modules/desktop_capture/win/screen_capture_utils.h @@ -29,6 +29,9 @@ WEBRTC_DECLARE_HANDLE(HMONITOR); namespace webrtc { +// Returns true if the system has at least one active display. +bool HasActiveDisplay(); + // Output the list of active screens into `screens`. Returns true if succeeded, // or false if it fails to enumerate the display devices. If the `device_names` // is provided, it will be filled with the DISPLAY_DEVICE.DeviceName in UTF-8 diff --git a/modules/desktop_capture/win/screen_capture_utils_unittest.cc b/modules/desktop_capture/win/screen_capture_utils_unittest.cc index 80d1fb3242..2e58c6b164 100644 --- a/modules/desktop_capture/win/screen_capture_utils_unittest.cc +++ b/modules/desktop_capture/win/screen_capture_utils_unittest.cc @@ -34,8 +34,9 @@ TEST(ScreenCaptureUtilsTest, GetScreenList) { TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) { DesktopCapturer::SourceList screens; ASSERT_TRUE(GetScreenList(&screens)); - if (screens.size() == 0) { - RTC_LOG(LS_INFO) << "Skip screen capture test on systems with no monitors."; + if (screens.empty()) { + RTC_LOG(LS_INFO) + << "Skip ScreenCaptureUtilsTest on systems with no monitors."; GTEST_SKIP(); } @@ -45,12 +46,33 @@ TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) { } TEST(ScreenCaptureUtilsTest, FullScreenDeviceIndexToHmonitor) { + if (!HasActiveDisplay()) { + RTC_LOG(LS_INFO) + << "Skip ScreenCaptureUtilsTest on systems with no monitors."; + GTEST_SKIP(); + } + HMONITOR hmonitor; ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor)); ASSERT_EQ(hmonitor, static_cast(0)); ASSERT_TRUE(IsMonitorValid(hmonitor)); } +TEST(ScreenCaptureUtilsTest, NoMonitors) { + if (HasActiveDisplay()) { + RTC_LOG(LS_INFO) << "Skip ScreenCaptureUtilsTest designed specifically for " + "systems with no monitors"; + GTEST_SKIP(); + } + + HMONITOR hmonitor; + ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor)); + ASSERT_EQ(hmonitor, static_cast(0)); + + // The monitor should be invalid since the system has no attached displays. + ASSERT_FALSE(IsMonitorValid(hmonitor)); +} + TEST(ScreenCaptureUtilsTest, InvalidDeviceIndexToHmonitor) { HMONITOR hmonitor; ASSERT_FALSE(GetHmonitorFromDeviceIndex(kInvalidScreenId, &hmonitor)); diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.cc b/modules/desktop_capture/win/screen_capturer_win_directx.cc index 1556d7c787..efa763993a 100644 --- a/modules/desktop_capture/win/screen_capturer_win_directx.cc +++ b/modules/desktop_capture/win/screen_capturer_win_directx.cc @@ -125,17 +125,23 @@ void ScreenCapturerWinDirectx::CaptureFrame() { int64_t capture_start_time_nanos = rtc::TimeNanos(); - frames_.MoveToNextFrame(); - if (!frames_.current_frame()) { - frames_.ReplaceCurrentFrame( + // Note that the [] operator will create the ScreenCaptureFrameQueue if it + // doesn't exist, so this is safe. + ScreenCaptureFrameQueue& frames = + frame_queue_map_[current_screen_id_]; + + frames.MoveToNextFrame(); + + if (!frames.current_frame()) { + frames.ReplaceCurrentFrame( std::make_unique(shared_memory_factory_.get())); } DxgiDuplicatorController::Result result; if (current_screen_id_ == kFullDesktopScreenId) { - result = controller_->Duplicate(frames_.current_frame()); + result = controller_->Duplicate(frames.current_frame()); } else { - result = controller_->DuplicateMonitor(frames_.current_frame(), + result = controller_->DuplicateMonitor(frames.current_frame(), current_screen_id_); } @@ -172,7 +178,7 @@ void ScreenCapturerWinDirectx::CaptureFrame() { } case DuplicateResult::SUCCEEDED: { std::unique_ptr frame = - frames_.current_frame()->frame()->Share(); + frames.current_frame()->frame()->Share(); int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) / rtc::kNumNanosecsPerMillisec; diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.h b/modules/desktop_capture/win/screen_capturer_win_directx.h index d64913ed10..801a0632fc 100644 --- a/modules/desktop_capture/win/screen_capturer_win_directx.h +++ b/modules/desktop_capture/win/screen_capturer_win_directx.h @@ -14,6 +14,7 @@ #include #include +#include #include #include "api/scoped_refptr.h" @@ -86,7 +87,14 @@ class RTC_EXPORT ScreenCapturerWinDirectx : public DesktopCapturer { private: const rtc::scoped_refptr controller_; - ScreenCaptureFrameQueue frames_; + + // The underlying DxgiDuplicators may retain a reference to the frames that + // we ask them to duplicate so that they can continue returning valid frames + // in the event that the target has not been updated. Thus, we need to ensure + // that we have a separate frame queue for each source id, so that these held + // frames don't get overwritten with the data from another Duplicator/monitor. + std::unordered_map> + frame_queue_map_; std::unique_ptr shared_memory_factory_; Callback* callback_ = nullptr; SourceId current_screen_id_ = kFullDesktopScreenId; diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc index 09c336fdc5..f89457372b 100644 --- a/modules/desktop_capture/win/wgc_capture_session.cc +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -12,7 +12,9 @@ #include #include -#include +#include +#include +#include #include #include @@ -93,8 +95,11 @@ void RecordGetFrameResult(GetFrameResult error) { } // namespace WgcCaptureSession::WgcCaptureSession(ComPtr d3d11_device, - ComPtr item) - : d3d11_device_(std::move(d3d11_device)), item_(std::move(item)) {} + ComPtr item, + ABI::Windows::Graphics::SizeInt32 size) + : d3d11_device_(std::move(d3d11_device)), + item_(std::move(item)), + size_(size) {} WgcCaptureSession::~WgcCaptureSession() = default; HRESULT WgcCaptureSession::StartCapture() { @@ -144,7 +149,7 @@ HRESULT WgcCaptureSession::StartCapture() { ComPtr frame_pool_statics; hr = GetActivationFactory< - ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics, + WGC::IDirect3D11CaptureFramePoolStatics, RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>( &frame_pool_statics); if (FAILED(hr)) { @@ -162,18 +167,8 @@ HRESULT WgcCaptureSession::StartCapture() { return hr; } - ABI::Windows::Graphics::SizeInt32 item_size; - hr = item_.Get()->get_Size(&item_size); - if (FAILED(hr)) { - RecordStartCaptureResult(StartCaptureResult::kGetItemSizeFailed); - return hr; - } - - previous_size_ = item_size; - - hr = frame_pool_statics2->CreateFreeThreaded(direct3d_device_.Get(), - kPixelFormat, kNumBuffers, - item_size, &frame_pool_); + hr = frame_pool_statics2->CreateFreeThreaded( + direct3d_device_.Get(), kPixelFormat, kNumBuffers, size_, &frame_pool_); if (FAILED(hr)) { RecordStartCaptureResult(StartCaptureResult::kCreateFreeThreadedFailed); return hr; @@ -223,8 +218,8 @@ HRESULT WgcCaptureSession::GetFrame( return hr; } - // We need to get this CaptureFrame as an ID3D11Texture2D so that we can get - // the raw image data in the format required by the DesktopFrame interface. + // We need to get `capture_frame` as an `ID3D11Texture2D` so that we can get + // the raw image data in the format required by the `DesktopFrame` interface. ComPtr d3d_surface; hr = capture_frame->get_Surface(&d3d_surface); @@ -261,16 +256,6 @@ HRESULT WgcCaptureSession::GetFrame( // Otherwise it would only be readable by the GPU. ComPtr d3d_context; d3d11_device_->GetImmediateContext(&d3d_context); - d3d_context->CopyResource(mapped_texture_.Get(), texture_2D.Get()); - - D3D11_MAPPED_SUBRESOURCE map_info; - hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0, - D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0, - &map_info); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kMapFrameFailed); - return hr; - } ABI::Windows::Graphics::SizeInt32 new_size; hr = capture_frame->get_ContentSize(&new_size); @@ -279,11 +264,52 @@ HRESULT WgcCaptureSession::GetFrame( return hr; } + // If the size changed, we must resize `mapped_texture_` and `frame_pool_` to + // fit the new size. This must be done before `CopySubresourceRegion` so that + // the textures are the same size. + if (size_.Height != new_size.Height || size_.Width != new_size.Width) { + hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed); + return hr; + } + + hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat, + kNumBuffers, new_size); + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed); + return hr; + } + } + // If the size has changed since the last capture, we must be sure to use // the smaller dimensions. Otherwise we might overrun our buffer, or // read stale data from the last frame. - int image_height = std::min(previous_size_.Height, new_size.Height); - int image_width = std::min(previous_size_.Width, new_size.Width); + int image_height = std::min(size_.Height, new_size.Height); + int image_width = std::min(size_.Width, new_size.Width); + + D3D11_BOX copy_region; + copy_region.left = 0; + copy_region.top = 0; + copy_region.right = image_width; + copy_region.bottom = image_height; + // Our textures are 2D so we just want one "slice" of the box. + copy_region.front = 0; + copy_region.back = 1; + d3d_context->CopySubresourceRegion(mapped_texture_.Get(), + /*dst_subresource_index=*/0, /*dst_x=*/0, + /*dst_y=*/0, /*dst_z=*/0, texture_2D.Get(), + /*src_subresource_index=*/0, ©_region); + + D3D11_MAPPED_SUBRESOURCE map_info; + hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0, + D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0, + &map_info); + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kMapFrameFailed); + return hr; + } + int row_data_length = image_width * DesktopFrame::kBytesPerPixel; // Make a copy of the data pointed to by `map_info.pData` so we are free to @@ -298,34 +324,15 @@ HRESULT WgcCaptureSession::GetFrame( src_data += map_info.RowPitch; } + d3d_context->Unmap(mapped_texture_.Get(), 0); + // Transfer ownership of `image_data` to the output_frame. DesktopSize size(image_width, image_height); *output_frame = std::make_unique(size, row_data_length, std::move(image_data)); - d3d_context->Unmap(mapped_texture_.Get(), 0); - - // If the size changed, we must resize the texture and frame pool to fit the - // new size. - if (previous_size_.Height != new_size.Height || - previous_size_.Width != new_size.Width) { - hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed); - return hr; - } - - hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat, - kNumBuffers, new_size); - if (FAILED(hr)) { - RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed); - return hr; - } - } - + size_ = new_size; RecordGetFrameResult(GetFrameResult::kSuccess); - - previous_size_ = new_size; return hr; } diff --git a/modules/desktop_capture/win/wgc_capture_session.h b/modules/desktop_capture/win/wgc_capture_session.h index e1456b2dd9..0b9d082823 100644 --- a/modules/desktop_capture/win/wgc_capture_session.h +++ b/modules/desktop_capture/win/wgc_capture_session.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -28,7 +29,8 @@ class WgcCaptureSession final { WgcCaptureSession( Microsoft::WRL::ComPtr d3d11_device, Microsoft::WRL::ComPtr< - ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item); + ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item, + ABI::Windows::Graphics::SizeInt32 size); // Disallow copy and assign. WgcCaptureSession(const WgcCaptureSession&) = delete; @@ -87,10 +89,10 @@ class WgcCaptureSession final { // frame. Microsoft::WRL::ComPtr mapped_texture_; - // This lets us know when the source has been resized, which is important - // because we must resize the framepool and our texture to be able to hold - // enough data for the frame. - ABI::Windows::Graphics::SizeInt32 previous_size_; + // This is the size of `mapped_texture_` and the buffers in `frame_pool_`. We + // store this as a member so we can compare it to the size of incoming frames + // and resize if necessary. + ABI::Windows::Graphics::SizeInt32 size_; // The capture session lets us set properties about the capture before it // starts such as whether to capture the mouse cursor, and it lets us tell WGC diff --git a/modules/desktop_capture/win/wgc_capture_source.cc b/modules/desktop_capture/win/wgc_capture_source.cc index c81cfcbf7b..24e6129ec7 100644 --- a/modules/desktop_capture/win/wgc_capture_source.cc +++ b/modules/desktop_capture/win/wgc_capture_source.cc @@ -10,6 +10,7 @@ #include "modules/desktop_capture/win/wgc_capture_source.h" +#include #include #include @@ -40,6 +41,18 @@ bool WgcCaptureSource::FocusOnSource() { return false; } +ABI::Windows::Graphics::SizeInt32 WgcCaptureSource::GetSize() { + if (!item_) + return {0, 0}; + + ABI::Windows::Graphics::SizeInt32 item_size; + HRESULT hr = item_->get_Size(&item_size); + if (FAILED(hr)) + return {0, 0}; + + return item_size; +} + HRESULT WgcCaptureSource::GetCaptureItem( ComPtr* result) { HRESULT hr = S_OK; @@ -80,6 +93,18 @@ DesktopVector WgcWindowSource::GetTopLeft() { return window_rect.top_left(); } +ABI::Windows::Graphics::SizeInt32 WgcWindowSource::GetSize() { + RECT window_rect; + HRESULT hr = ::DwmGetWindowAttribute( + reinterpret_cast(GetSourceId()), DWMWA_EXTENDED_FRAME_BOUNDS, + reinterpret_cast(&window_rect), sizeof(window_rect)); + if (FAILED(hr)) + return WgcCaptureSource::GetSize(); + + return {window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top}; +} + bool WgcWindowSource::IsCapturable() { if (!IsWindowValidAndVisible(reinterpret_cast(GetSourceId()))) return false; @@ -138,6 +163,15 @@ DesktopVector WgcScreenSource::GetTopLeft() { return GetMonitorRect(*hmonitor_).top_left(); } +ABI::Windows::Graphics::SizeInt32 WgcScreenSource::GetSize() { + ABI::Windows::Graphics::SizeInt32 size = WgcCaptureSource::GetSize(); + if (!hmonitor_ || (size.Width != 0 && size.Height != 0)) + return size; + + DesktopRect rect = GetMonitorRect(*hmonitor_); + return {rect.width(), rect.height()}; +} + bool WgcScreenSource::IsCapturable() { if (!hmonitor_) return false; @@ -163,6 +197,12 @@ HRESULT WgcScreenSource::CreateCaptureItem( if (FAILED(hr)) return hr; + // Ensure the monitor is still valid (hasn't disconnected) before trying to + // create the item. On versions of Windows before Win11, `CreateForMonitor` + // will crash if no displays are connected. + if (!IsMonitorValid(hmonitor_.value())) + return E_ABORT; + ComPtr item; hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item)); if (FAILED(hr)) diff --git a/modules/desktop_capture/win/wgc_capture_source.h b/modules/desktop_capture/win/wgc_capture_source.h index 44090ec4d8..d1275b6168 100644 --- a/modules/desktop_capture/win/wgc_capture_source.h +++ b/modules/desktop_capture/win/wgc_capture_source.h @@ -12,6 +12,7 @@ #define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_ #include +#include #include #include @@ -34,6 +35,7 @@ class WgcCaptureSource { virtual DesktopVector GetTopLeft() = 0; virtual bool IsCapturable(); virtual bool FocusOnSource(); + virtual ABI::Windows::Graphics::SizeInt32 GetSize(); HRESULT GetCaptureItem( Microsoft::WRL::ComPtr< ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result); @@ -96,6 +98,7 @@ class WgcWindowSource final : public WgcCaptureSource { ~WgcWindowSource() override; DesktopVector GetTopLeft() override; + ABI::Windows::Graphics::SizeInt32 GetSize() override; bool IsCapturable() override; bool FocusOnSource() override; @@ -117,6 +120,7 @@ class WgcScreenSource final : public WgcCaptureSource { ~WgcScreenSource() override; DesktopVector GetTopLeft() override; + ABI::Windows::Graphics::SizeInt32 GetSize() override; bool IsCapturable() override; private: diff --git a/modules/desktop_capture/win/wgc_capture_source_unittest.cc b/modules/desktop_capture/win/wgc_capture_source_unittest.cc index a230e12578..dc37ec2e0d 100644 --- a/modules/desktop_capture/win/wgc_capture_source_unittest.cc +++ b/modules/desktop_capture/win/wgc_capture_source_unittest.cc @@ -19,10 +19,10 @@ #include "modules/desktop_capture/desktop_geometry.h" #include "modules/desktop_capture/win/screen_capture_utils.h" #include "modules/desktop_capture/win/test_support/test_window.h" +#include "modules/desktop_capture/win/wgc_capturer_win.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/win/scoped_com_initializer.h" -#include "rtc_base/win/windows_version.h" #include "test/gtest.h" namespace webrtc { @@ -35,19 +35,11 @@ const int kFirstYCoord = 50; const int kSecondXCoord = 50; const int kSecondYCoord = 75; -enum SourceType { kWindowSource = 0, kScreenSource = 1 }; - } // namespace -class WgcCaptureSourceTest : public ::testing::TestWithParam { +class WgcCaptureSourceTest : public ::testing::TestWithParam { public: void SetUp() override { - if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) { - RTC_LOG(LS_INFO) - << "Skipping WgcCaptureSourceTests on Windows versions < RS5."; - GTEST_SKIP(); - } - com_initializer_ = std::make_unique(ScopedCOMInitializer::kMTA); ASSERT_TRUE(com_initializer_->Succeeded()); @@ -82,6 +74,12 @@ class WgcCaptureSourceTest : public ::testing::TestWithParam { // Window specific test TEST_F(WgcCaptureSourceTest, WindowPosition) { + if (!IsWgcSupported(CaptureType::kWindow)) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } + SetUpForWindowSource(); source_ = source_factory_->CreateCaptureSource(source_id_); ASSERT_TRUE(source_); @@ -100,6 +98,12 @@ TEST_F(WgcCaptureSourceTest, WindowPosition) { // Screen specific test TEST_F(WgcCaptureSourceTest, ScreenPosition) { + if (!IsWgcSupported(CaptureType::kScreen)) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } + SetUpForScreenSource(); source_ = source_factory_->CreateCaptureSource(source_id_); ASSERT_TRUE(source_); @@ -113,7 +117,13 @@ TEST_F(WgcCaptureSourceTest, ScreenPosition) { // Source agnostic test TEST_P(WgcCaptureSourceTest, CreateSource) { - if (GetParam() == SourceType::kWindowSource) { + if (!IsWgcSupported(GetParam())) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } + + if (GetParam() == CaptureType::kWindow) { SetUpForWindowSource(); } else { SetUpForScreenSource(); @@ -132,7 +142,7 @@ TEST_P(WgcCaptureSourceTest, CreateSource) { INSTANTIATE_TEST_SUITE_P(SourceAgnostic, WgcCaptureSourceTest, - ::testing::Values(SourceType::kWindowSource, - SourceType::kScreenSource)); + ::testing::Values(CaptureType::kWindow, + CaptureType::kScreen)); } // namespace webrtc diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc index 9b2cce4a8e..eacaa893d9 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.cc +++ b/modules/desktop_capture/win/wgc_capturer_win.cc @@ -10,6 +10,9 @@ #include "modules/desktop_capture/win/wgc_capturer_win.h" +#include +#include + #include #include "modules/desktop_capture/desktop_capture_metrics_helper.h" @@ -17,6 +20,9 @@ #include "modules/desktop_capture/win/wgc_desktop_frame.h" #include "rtc_base/logging.h" #include "rtc_base/time_utils.h" +#include "rtc_base/win/get_activation_factory.h" +#include "rtc_base/win/hstring.h" +#include "rtc_base/win/windows_version.h" #include "system_wrappers/include/metrics.h" namespace WGC = ABI::Windows::Graphics::Capture; @@ -26,6 +32,11 @@ namespace webrtc { namespace { +const wchar_t kWgcSessionType[] = + L"Windows.Graphics.Capture.GraphicsCaptureSession"; +const wchar_t kApiContract[] = L"Windows.Foundation.UniversalApiContract"; +const UINT16 kRequiredApiContractVersion = 8; + enum class WgcCapturerResult { kSuccess = 0, kNoDirect3dDevice = 1, @@ -45,20 +56,88 @@ void RecordWgcCapturerResult(WgcCapturerResult error) { } // namespace +bool IsWgcSupported(CaptureType capture_type) { + if (capture_type == CaptureType::kScreen) { + // A bug in the WGC API `CreateForMonitor` was fixed in 20H1. + if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1) + return false; + + // There is another bug in `CreateForMonitor` that causes a crash if there + // are no active displays. + if (!HasActiveDisplay()) + return false; + } + + if (!ResolveCoreWinRTDelayload()) + return false; + + ComPtr + api_info_statics; + HRESULT hr = GetActivationFactory< + ABI::Windows::Foundation::Metadata::IApiInformationStatics, + RuntimeClass_Windows_Foundation_Metadata_ApiInformation>( + &api_info_statics); + if (FAILED(hr)) + return false; + + HSTRING api_contract; + hr = webrtc::CreateHstring(kApiContract, wcslen(kApiContract), &api_contract); + if (FAILED(hr)) + return false; + + boolean is_api_present; + hr = api_info_statics->IsApiContractPresentByMajor( + api_contract, kRequiredApiContractVersion, &is_api_present); + webrtc::DeleteHstring(api_contract); + if (FAILED(hr) || !is_api_present) + return false; + + HSTRING wgc_session_type; + hr = webrtc::CreateHstring(kWgcSessionType, wcslen(kWgcSessionType), + &wgc_session_type); + if (FAILED(hr)) + return false; + + boolean is_type_present; + hr = api_info_statics->IsTypePresent(wgc_session_type, &is_type_present); + webrtc::DeleteHstring(wgc_session_type); + if (FAILED(hr) || !is_type_present) + return false; + + ComPtr capture_session_statics; + hr = GetActivationFactory< + WGC::IGraphicsCaptureSessionStatics, + RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession>( + &capture_session_statics); + if (FAILED(hr)) + return false; + + boolean is_supported; + hr = capture_session_statics->IsSupported(&is_supported); + if (FAILED(hr) || !is_supported) + return false; + + return true; +} + WgcCapturerWin::WgcCapturerWin( std::unique_ptr source_factory, - std::unique_ptr source_enumerator) + std::unique_ptr source_enumerator, + bool allow_delayed_capturable_check) : source_factory_(std::move(source_factory)), - source_enumerator_(std::move(source_enumerator)) {} + source_enumerator_(std::move(source_enumerator)), + allow_delayed_capturable_check_(allow_delayed_capturable_check) {} WgcCapturerWin::~WgcCapturerWin() = default; // static std::unique_ptr WgcCapturerWin::CreateRawWindowCapturer( - const DesktopCaptureOptions& options) { + const DesktopCaptureOptions& options, + bool allow_delayed_capturable_check) { return std::make_unique( std::make_unique(), std::make_unique( - options.enumerate_current_process_windows())); + options.enumerate_current_process_windows()), + allow_delayed_capturable_check); } // static @@ -66,7 +145,7 @@ std::unique_ptr WgcCapturerWin::CreateRawScreenCapturer( const DesktopCaptureOptions& options) { return std::make_unique( std::make_unique(), - std::make_unique()); + std::make_unique(), false); } bool WgcCapturerWin::GetSourceList(SourceList* sources) { @@ -75,6 +154,9 @@ bool WgcCapturerWin::GetSourceList(SourceList* sources) { bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { capture_source_ = source_factory_->CreateCaptureSource(id); + if (allow_delayed_capturable_check_) + return true; + return capture_source_->IsCapturable(); } @@ -135,6 +217,13 @@ void WgcCapturerWin::CaptureFrame() { return; } + if (allow_delayed_capturable_check_ && !capture_source_->IsCapturable()) { + RTC_LOG(LS_ERROR) << "Source is not capturable."; + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + /*frame=*/nullptr); + return; + } + int64_t capture_start_time_nanos = rtc::TimeNanos(); HRESULT hr; @@ -156,7 +245,8 @@ void WgcCapturerWin::CaptureFrame() { iter_success_pair = ongoing_captures_.emplace( std::piecewise_construct, std::forward_as_tuple(capture_source_->GetSourceId()), - std::forward_as_tuple(d3d11_device_, item)); + std::forward_as_tuple(d3d11_device_, item, + capture_source_->GetSize())); RTC_DCHECK(iter_success_pair.second); capture_session = &iter_success_pair.first->second; } else { diff --git a/modules/desktop_capture/win/wgc_capturer_win.h b/modules/desktop_capture/win/wgc_capturer_win.h index 34e6874dc0..ba212b6e69 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.h +++ b/modules/desktop_capture/win/wgc_capturer_win.h @@ -26,6 +26,9 @@ namespace webrtc { +// Checks if the WGC API is present and supported on the system. +bool IsWgcSupported(CaptureType capture_type); + // WgcCapturerWin is initialized with an implementation of this base class, // which it uses to find capturable sources of a particular type. This way, // WgcCapturerWin can remain source-agnostic. @@ -80,7 +83,8 @@ class ScreenEnumerator final : public SourceEnumerator { class WgcCapturerWin : public DesktopCapturer { public: WgcCapturerWin(std::unique_ptr source_factory, - std::unique_ptr source_enumerator); + std::unique_ptr source_enumerator, + bool allow_delayed_capturable_check); WgcCapturerWin(const WgcCapturerWin&) = delete; WgcCapturerWin& operator=(const WgcCapturerWin&) = delete; @@ -88,7 +92,8 @@ class WgcCapturerWin : public DesktopCapturer { ~WgcCapturerWin() override; static std::unique_ptr CreateRawWindowCapturer( - const DesktopCaptureOptions& options); + const DesktopCaptureOptions& options, + bool allow_delayed_capturable_check = false); static std::unique_ptr CreateRawScreenCapturer( const DesktopCaptureOptions& options); @@ -128,6 +133,11 @@ class WgcCapturerWin : public DesktopCapturer { // returns. Callback* callback_ = nullptr; + // WgcCaptureSource::IsCapturable is expensive to run. So, caller can + // delay capturable check till capture frame is called if the WgcCapturerWin + // is used as a fallback capturer. + bool allow_delayed_capturable_check_ = false; + // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who // require one to perform the capture. Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index fe18577ae9..b83266cda0 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -24,8 +24,8 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/win/scoped_com_initializer.h" -#include "rtc_base/win/windows_version.h" #include "system_wrappers/include/metrics.h" +#include "system_wrappers/include/sleep.h" #include "test/gtest.h" namespace webrtc { @@ -63,11 +63,8 @@ const int kWindowHeightSubtrahend = 7; // Custom message constants so we can direct our thread to close windows // and quit running. -const UINT kNoOp = WM_APP; -const UINT kDestroyWindow = WM_APP + 1; -const UINT kQuitRunning = WM_APP + 2; - -enum CaptureType { kWindowCapture = 0, kScreenCapture = 1 }; +const UINT kDestroyWindow = WM_APP; +const UINT kQuitRunning = WM_APP + 1; } // namespace @@ -75,15 +72,15 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, public DesktopCapturer::Callback { public: void SetUp() override { - if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_RS5) { - RTC_LOG(LS_INFO) - << "Skipping WgcCapturerWinTests on Windows versions < RS5."; - GTEST_SKIP(); - } - com_initializer_ = std::make_unique(ScopedCOMInitializer::kMTA); EXPECT_TRUE(com_initializer_->Succeeded()); + + if (!IsWgcSupported(GetParam())) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } } void SetUpForWindowCapture(int window_width = kMediumWindowWidth, @@ -164,18 +161,18 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, // Frequently, the test window will not show up in GetSourceList because it // was created too recently. Since we are confident the window will be found // eventually we loop here until we find it. - intptr_t src_id; + intptr_t src_id = 0; do { DesktopCapturer::SourceList sources; EXPECT_TRUE(capturer_->GetSourceList(&sources)); - auto it = std::find_if( sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) { return src.id == reinterpret_cast(window_info_.hwnd); }); - src_id = it->id; + if (it != sources.end()) + src_id = it->id; } while (src_id != reinterpret_cast(window_info_.hwnd)); return src_id; @@ -260,7 +257,7 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, }; TEST_P(WgcCapturerWinTest, SelectValidSource) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -270,7 +267,7 @@ TEST_P(WgcCapturerWinTest, SelectValidSource) { } TEST_P(WgcCapturerWinTest, SelectInvalidSource) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { capturer_ = WgcCapturerWin::CreateRawWindowCapturer( DesktopCaptureOptions::CreateDefault()); source_id_ = kNullWindowId; @@ -284,7 +281,7 @@ TEST_P(WgcCapturerWinTest, SelectInvalidSource) { } TEST_P(WgcCapturerWinTest, Capture) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -303,7 +300,7 @@ TEST_P(WgcCapturerWinTest, Capture) { } TEST_P(WgcCapturerWinTest, CaptureTime) { - if (GetParam() == CaptureType::kWindowCapture) { + if (GetParam() == CaptureType::kWindow) { SetUpForWindowCapture(); } else { SetUpForScreenCapture(); @@ -331,11 +328,37 @@ TEST_P(WgcCapturerWinTest, CaptureTime) { INSTANTIATE_TEST_SUITE_P(SourceAgnostic, WgcCapturerWinTest, - ::testing::Values(CaptureType::kWindowCapture, - CaptureType::kScreenCapture)); + ::testing::Values(CaptureType::kWindow, + CaptureType::kScreen)); + +TEST(WgcCapturerNoMonitorTest, NoMonitors) { + if (HasActiveDisplay()) { + RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for " + "systems with no monitors"; + GTEST_SKIP(); + } + + // A bug in `CreateForMonitor` prevents screen capture when no displays are + // attached. + EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen)); +} + +class WgcCapturerMonitorTest : public WgcCapturerWinTest { + public: + void SetUp() { + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); + EXPECT_TRUE(com_initializer_->Succeeded()); -// Monitor specific tests. -TEST_F(WgcCapturerWinTest, FocusOnMonitor) { + if (!IsWgcSupported(CaptureType::kScreen)) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } + } +}; + +TEST_F(WgcCapturerMonitorTest, FocusOnMonitor) { SetUpForScreenCapture(); EXPECT_TRUE(capturer_->SelectSource(0)); @@ -343,7 +366,7 @@ TEST_F(WgcCapturerWinTest, FocusOnMonitor) { EXPECT_FALSE(capturer_->FocusOnSelectedSource()); } -TEST_F(WgcCapturerWinTest, CaptureAllMonitors) { +TEST_F(WgcCapturerMonitorTest, CaptureAllMonitors) { SetUpForScreenCapture(); EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId)); @@ -353,8 +376,22 @@ TEST_F(WgcCapturerWinTest, CaptureAllMonitors) { EXPECT_GT(frame_->size().height(), 0); } -// Window specific tests. -TEST_F(WgcCapturerWinTest, FocusOnWindow) { +class WgcCapturerWindowTest : public WgcCapturerWinTest { + public: + void SetUp() { + com_initializer_ = + std::make_unique(ScopedCOMInitializer::kMTA); + EXPECT_TRUE(com_initializer_->Succeeded()); + + if (!IsWgcSupported(CaptureType::kWindow)) { + RTC_LOG(LS_INFO) + << "Skipping WgcCapturerWinTests on unsupported platforms."; + GTEST_SKIP(); + } + } +}; + +TEST_F(WgcCapturerWindowTest, FocusOnWindow) { capturer_ = WgcCapturerWin::CreateRawWindowCapturer( DesktopCaptureOptions::CreateDefault()); window_info_ = CreateTestWindow(kWindowTitle); @@ -370,7 +407,7 @@ TEST_F(WgcCapturerWinTest, FocusOnWindow) { DestroyTestWindow(window_info_); } -TEST_F(WgcCapturerWinTest, SelectMinimizedWindow) { +TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) { SetUpForWindowCapture(); MinimizeTestWindow(reinterpret_cast(source_id_)); EXPECT_FALSE(capturer_->SelectSource(source_id_)); @@ -379,7 +416,7 @@ TEST_F(WgcCapturerWinTest, SelectMinimizedWindow) { EXPECT_TRUE(capturer_->SelectSource(source_id_)); } -TEST_F(WgcCapturerWinTest, SelectClosedWindow) { +TEST_F(WgcCapturerWindowTest, SelectClosedWindow) { SetUpForWindowCapture(); EXPECT_TRUE(capturer_->SelectSource(source_id_)); @@ -387,7 +424,7 @@ TEST_F(WgcCapturerWinTest, SelectClosedWindow) { EXPECT_FALSE(capturer_->SelectSource(source_id_)); } -TEST_F(WgcCapturerWinTest, UnsupportedWindowStyle) { +TEST_F(WgcCapturerWindowTest, UnsupportedWindowStyle) { // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not // support. window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth, @@ -406,7 +443,7 @@ TEST_F(WgcCapturerWinTest, UnsupportedWindowStyle) { DestroyTestWindow(window_info_); } -TEST_F(WgcCapturerWinTest, IncreaseWindowSizeMidCapture) { +TEST_F(WgcCapturerWindowTest, IncreaseWindowSizeMidCapture) { SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight); EXPECT_TRUE(capturer_->SelectSource(source_id_)); @@ -427,7 +464,7 @@ TEST_F(WgcCapturerWinTest, IncreaseWindowSizeMidCapture) { ValidateFrame(kLargeWindowWidth, kMediumWindowHeight); } -TEST_F(WgcCapturerWinTest, ReduceWindowSizeMidCapture) { +TEST_F(WgcCapturerWindowTest, ReduceWindowSizeMidCapture) { SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight); EXPECT_TRUE(capturer_->SelectSource(source_id_)); @@ -446,7 +483,7 @@ TEST_F(WgcCapturerWinTest, ReduceWindowSizeMidCapture) { ValidateFrame(kSmallWindowWidth, kMediumWindowHeight); } -TEST_F(WgcCapturerWinTest, MinimizeWindowMidCapture) { +TEST_F(WgcCapturerWindowTest, MinimizeWindowMidCapture) { SetUpForWindowCapture(); EXPECT_TRUE(capturer_->SelectSource(source_id_)); @@ -467,7 +504,7 @@ TEST_F(WgcCapturerWinTest, MinimizeWindowMidCapture) { // a good test. } -TEST_F(WgcCapturerWinTest, CloseWindowMidCapture) { +TEST_F(WgcCapturerWindowTest, CloseWindowMidCapture) { SetUpForWindowCapture(); EXPECT_TRUE(capturer_->SelectSource(source_id_)); @@ -477,18 +514,17 @@ TEST_F(WgcCapturerWinTest, CloseWindowMidCapture) { CloseTestWindow(); - // We need to call GetMessage to trigger the Closed event and the capturer's - // event handler for it. If we are too early and the Closed event hasn't - // arrived yet we should keep trying until the capturer receives it and stops. + // We need to pump our message queue so the Closed event will be delivered to + // the capturer's event handler. If we are too early and the Closed event + // hasn't arrived yet we should keep trying until the capturer receives it and + // stops. auto* wgc_capturer = static_cast(capturer_.get()); + MSG msg; while (wgc_capturer->IsSourceBeingCaptured(source_id_)) { - // Since the capturer handles the Closed message, there will be no message - // for us and GetMessage will hang, unless we send ourselves a message - // first. - ::PostThreadMessage(GetCurrentThreadId(), kNoOp, 0, 0); - MSG msg; - ::GetMessage(&msg, NULL, 0, 0); - ::DispatchMessage(&msg); + // Unlike GetMessage, PeekMessage will not hang if there are no messages in + // the queue. + PeekMessage(&msg, 0, 0, 0, PM_REMOVE); + SleepMs(1); } // Occasionally, one last frame will have made it into the frame pool before diff --git a/modules/desktop_capture/window_capturer_win.cc b/modules/desktop_capture/window_capturer_win.cc index 4bfa09f4d6..f289746e30 100644 --- a/modules/desktop_capture/window_capturer_win.cc +++ b/modules/desktop_capture/window_capturer_win.cc @@ -12,12 +12,37 @@ #include "modules/desktop_capture/desktop_capturer.h" #include "modules/desktop_capture/win/window_capturer_win_gdi.h" +#if defined(RTC_ENABLE_WIN_WGC) +#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h" +#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h" +#include "modules/desktop_capture/win/wgc_capturer_win.h" +#include "rtc_base/win/windows_version.h" +#endif // defined(RTC_ENABLE_WIN_WGC) + namespace webrtc { // static std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( const DesktopCaptureOptions& options) { - return WindowCapturerWinGdi::CreateRawWindowCapturer(options); + std::unique_ptr capturer( + WindowCapturerWinGdi::CreateRawWindowCapturer(options)); +#if defined(RTC_ENABLE_WIN_WGC) + if (options.allow_wgc_capturer_fallback() && + rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN11) { + // BlankDectector capturer will send an error when it detects a failed + // GDI rendering, then Fallback capturer will try to capture it again with + // WGC. + capturer = std::make_unique( + std::move(capturer), RgbaColor(0, 0, 0, 0), + /*check_per_capture*/ true); + + capturer = std::make_unique( + std::move(capturer), + WgcCapturerWin::CreateRawWindowCapturer( + options, /*allow_delayed_capturable_check*/ true)); + } +#endif // defined(RTC_ENABLE_WIN_WGC) + return capturer; } } // namespace webrtc diff --git a/modules/pacing/BUILD.gn b/modules/pacing/BUILD.gn index 013c2de599..eaf2427b10 100644 --- a/modules/pacing/BUILD.gn +++ b/modules/pacing/BUILD.gn @@ -23,6 +23,8 @@ rtc_library("pacing") { "pacing_controller.h", "packet_router.cc", "packet_router.h", + "prioritized_packet_queue.cc", + "prioritized_packet_queue.h", "round_robin_packet_queue.cc", "round_robin_packet_queue.h", "rtp_packet_pacer.h", @@ -33,13 +35,14 @@ rtc_library("pacing") { deps = [ ":interval_budget", "..:module_api", + "../../api:field_trials_view", + "../../api:field_trials_view", "../../api:function_view", "../../api:sequence_checker", "../../api/rtc_event_log", "../../api/task_queue:task_queue", "../../api/transport:field_trial_based_config", "../../api/transport:network_control", - "../../api/transport:webrtc_key_value_config", "../../api/units:data_rate", "../../api/units:data_size", "../../api/units:time_delta", @@ -47,9 +50,13 @@ rtc_library("pacing") { "../../logging:rtc_event_bwe", "../../logging:rtc_event_pacing", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:location", + "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", + "../../rtc_base:timeutils", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/synchronization:mutex", "../../rtc_base/task_utils:to_queued_task", @@ -74,7 +81,7 @@ rtc_library("interval_budget") { deps = [ "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:safe_conversions", ] } @@ -88,23 +95,24 @@ if (rtc_include_tests) { "paced_sender_unittest.cc", "pacing_controller_unittest.cc", "packet_router_unittest.cc", + "prioritized_packet_queue_unittest.cc", "task_queue_paced_sender_unittest.cc", ] deps = [ ":interval_budget", ":pacing", + "../../api/task_queue:task_queue", "../../api/transport:network_control", "../../api/units:data_rate", "../../api/units:time_delta", "../../modules/utility:mock_process_thread", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_base_tests_utils", "../../rtc_base/experiments:alr_experiment", + "../../rtc_base/task_utils:to_queued_task", "../../system_wrappers", - "../../system_wrappers:field_trial", "../../test:explicit_key_value_config", - "../../test:field_trial", + "../../test:scoped_key_value_config", "../../test:test_support", "../../test/time_controller:time_controller", "../rtp_rtcp", diff --git a/modules/pacing/DEPS b/modules/pacing/DEPS index 1b2e6dcf45..42f3dfcb14 100644 --- a/modules/pacing/DEPS +++ b/modules/pacing/DEPS @@ -1,6 +1,6 @@ include_rules = [ "+system_wrappers", - # Avoid directly using field_trial. Instead use WebRtcKeyValueConfig. + # Avoid directly using field_trial. Instead use FieldTrialsView. "-system_wrappers/include/field_trial.h", "+logging/rtc_event_log" ] diff --git a/modules/pacing/bitrate_prober.cc b/modules/pacing/bitrate_prober.cc index ed4b7760c7..d2b93dabd0 100644 --- a/modules/pacing/bitrate_prober.cc +++ b/modules/pacing/bitrate_prober.cc @@ -33,7 +33,7 @@ constexpr TimeDelta kProbeClusterTimeout = TimeDelta::Seconds(5); } // namespace BitrateProberConfig::BitrateProberConfig( - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : min_probe_packets_sent("min_probe_packets_sent", 5), min_probe_delta("min_probe_delta", TimeDelta::Millis(1)), min_probe_duration("min_probe_duration", TimeDelta::Millis(15)), @@ -56,7 +56,7 @@ BitrateProber::~BitrateProber() { total_failed_probe_count_); } -BitrateProber::BitrateProber(const WebRtcKeyValueConfig& field_trials) +BitrateProber::BitrateProber(const FieldTrialsView& field_trials) : probing_state_(ProbingState::kDisabled), next_probe_time_(Timestamp::PlusInfinity()), total_probe_count_(0), diff --git a/modules/pacing/bitrate_prober.h b/modules/pacing/bitrate_prober.h index 3ac431cee3..94016d5250 100644 --- a/modules/pacing/bitrate_prober.h +++ b/modules/pacing/bitrate_prober.h @@ -24,7 +24,7 @@ namespace webrtc { class RtcEventLog; struct BitrateProberConfig { - explicit BitrateProberConfig(const WebRtcKeyValueConfig* key_value_config); + explicit BitrateProberConfig(const FieldTrialsView* key_value_config); BitrateProberConfig(const BitrateProberConfig&) = default; BitrateProberConfig& operator=(const BitrateProberConfig&) = default; ~BitrateProberConfig() = default; @@ -46,7 +46,7 @@ struct BitrateProberConfig { // on being protected by the caller. class BitrateProber { public: - explicit BitrateProber(const WebRtcKeyValueConfig& field_trials); + explicit BitrateProber(const FieldTrialsView& field_trials); ~BitrateProber(); void SetEnabled(bool enable); diff --git a/modules/pacing/paced_sender.cc b/modules/pacing/paced_sender.cc index acc492db92..22c86f7fbd 100644 --- a/modules/pacing/paced_sender.cc +++ b/modules/pacing/paced_sender.cc @@ -16,7 +16,6 @@ #include "absl/memory/memory.h" #include "absl/strings/match.h" -#include "api/rtc_event_log/rtc_event_log.h" #include "modules/utility/include/process_thread.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" @@ -31,20 +30,14 @@ const float PacedSender::kDefaultPaceMultiplier = 2.5f; PacedSender::PacedSender(Clock* clock, PacketRouter* packet_router, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const FieldTrialsView& field_trials, ProcessThread* process_thread) : process_mode_( - (field_trials != nullptr && - absl::StartsWith(field_trials->Lookup("WebRTC-Pacer-DynamicProcess"), - "Enabled")) + absl::StartsWith(field_trials.Lookup("WebRTC-Pacer-DynamicProcess"), + "Enabled") ? PacingController::ProcessMode::kDynamic : PacingController::ProcessMode::kPeriodic), - pacing_controller_(clock, - packet_router, - event_log, - field_trials, - process_mode_), + pacing_controller_(clock, packet_router, field_trials, process_mode_), clock_(clock), process_thread_(process_thread) { if (process_thread_) @@ -88,18 +81,10 @@ void PacedSender::Resume() { } } -void PacedSender::SetCongestionWindow(DataSize congestion_window_size) { +void PacedSender::SetCongested(bool congested) { { MutexLock lock(&mutex_); - pacing_controller_.SetCongestionWindow(congestion_window_size); - } - MaybeWakupProcessThread(); -} - -void PacedSender::UpdateOutstandingData(DataSize outstanding_data) { - { - MutexLock lock(&mutex_); - pacing_controller_.UpdateOutstandingData(outstanding_data); + pacing_controller_.SetCongested(congested); } MaybeWakupProcessThread(); } @@ -124,7 +109,7 @@ void PacedSender::EnqueuePackets( packet->SequenceNumber(), "rtp_timestamp", packet->Timestamp()); - RTC_DCHECK_GE(packet->capture_time_ms(), 0); + RTC_DCHECK_GE(packet->capture_time(), Timestamp::Zero()); pacing_controller_.EnqueuePacket(std::move(packet)); } } diff --git a/modules/pacing/paced_sender.h b/modules/pacing/paced_sender.h index 4a53e0f9ba..47fdaf3e41 100644 --- a/modules/pacing/paced_sender.h +++ b/modules/pacing/paced_sender.h @@ -19,10 +19,10 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/function_view.h" #include "api/transport/field_trial_based_config.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "modules/include/module.h" #include "modules/pacing/bitrate_prober.h" #include "modules/pacing/interval_budget.h" @@ -37,7 +37,6 @@ namespace webrtc { class Clock; -class RtcEventLog; class PacedSender : public RtpPacketPacer, public RtpPacketSender { public: @@ -57,8 +56,7 @@ class PacedSender : public RtpPacketPacer, public RtpPacketSender { // optional once all callers have been updated. PacedSender(Clock* clock, PacketRouter* packet_router, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials = nullptr, + const FieldTrialsView& field_trials, ProcessThread* process_thread = nullptr); ~PacedSender() override; @@ -80,8 +78,7 @@ class PacedSender : public RtpPacketPacer, public RtpPacketSender { // Resume sending packets. void Resume() override; - void SetCongestionWindow(DataSize congestion_window_size) override; - void UpdateOutstandingData(DataSize outstanding_data) override; + void SetCongested(bool congested) override; // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) override; diff --git a/modules/pacing/paced_sender_unittest.cc b/modules/pacing/paced_sender_unittest.cc index 53cc1c42ed..e833f34f0c 100644 --- a/modules/pacing/paced_sender_unittest.cc +++ b/modules/pacing/paced_sender_unittest.cc @@ -19,8 +19,6 @@ #include "modules/pacing/packet_router.h" #include "modules/utility/include/mock/mock_process_thread.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -50,7 +48,7 @@ class MockCallback : public PacketRouter { (override)); }; -class ProcessModeTrials : public WebRtcKeyValueConfig { +class ProcessModeTrials : public FieldTrialsView { public: explicit ProcessModeTrials(bool dynamic_process) : mode_(dynamic_process) {} @@ -80,8 +78,8 @@ class PacedSenderTest EXPECT_CALL(process_thread_, RegisterModule) .WillOnce(SaveArg<0>(&paced_module_)); - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - &trials_, &process_thread_); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + &process_thread_); EXPECT_CALL(process_thread_, WakeUp).WillRepeatedly([&](Module* module) { clock_.AdvanceTimeMilliseconds(module->TimeUntilNextProcess()); }); diff --git a/modules/pacing/pacing_controller.cc b/modules/pacing/pacing_controller.cc index 9215462239..4dcb0516e7 100644 --- a/modules/pacing/pacing_controller.cc +++ b/modules/pacing/pacing_controller.cc @@ -18,6 +18,8 @@ #include "absl/strings/match.h" #include "modules/pacing/bitrate_prober.h" #include "modules/pacing/interval_budget.h" +#include "modules/pacing/prioritized_packet_queue.h" +#include "modules/pacing/round_robin_packet_queue.h" #include "rtc_base/checks.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" @@ -38,24 +40,15 @@ constexpr TimeDelta kMaxElapsedTime = TimeDelta::Seconds(2); // time. Applies only to periodic mode. constexpr TimeDelta kMaxProcessingInterval = TimeDelta::Millis(30); -// Allow probes to be processed slightly ahead of inteded send time. Currently -// set to 1ms as this is intended to allow times be rounded down to the nearest -// millisecond. -constexpr TimeDelta kMaxEarlyProbeProcessing = TimeDelta::Millis(1); - -constexpr int kFirstPriority = 0; - -bool IsDisabled(const WebRtcKeyValueConfig& field_trials, - absl::string_view key) { +bool IsDisabled(const FieldTrialsView& field_trials, absl::string_view key) { return absl::StartsWith(field_trials.Lookup(key), "Disabled"); } -bool IsEnabled(const WebRtcKeyValueConfig& field_trials, - absl::string_view key) { +bool IsEnabled(const FieldTrialsView& field_trials, absl::string_view key) { return absl::StartsWith(field_trials.Lookup(key), "Enabled"); } -TimeDelta GetDynamicPaddingTarget(const WebRtcKeyValueConfig& field_trials) { +TimeDelta GetDynamicPaddingTarget(const FieldTrialsView& field_trials) { FieldTrialParameter padding_target("timedelta", TimeDelta::Millis(5)); ParseFieldTrial({&padding_target}, @@ -63,27 +56,13 @@ TimeDelta GetDynamicPaddingTarget(const WebRtcKeyValueConfig& field_trials) { return padding_target.Get(); } -int GetPriorityForType(RtpPacketMediaType type) { - // Lower number takes priority over higher. - switch (type) { - case RtpPacketMediaType::kAudio: - // Audio is always prioritized over other packet types. - return kFirstPriority + 1; - case RtpPacketMediaType::kRetransmission: - // Send retransmissions before new media. - return kFirstPriority + 2; - case RtpPacketMediaType::kVideo: - case RtpPacketMediaType::kForwardErrorCorrection: - // Video has "normal" priority, in the old speak. - // Send redundancy concurrently to video. If it is delayed it might have a - // lower chance of being useful. - return kFirstPriority + 3; - case RtpPacketMediaType::kPadding: - // Packets that are in themselves likely useless, only sent to keep the - // BWE high. - return kFirstPriority + 4; - } - RTC_CHECK_NOTREACHED(); +std::unique_ptr CreatePacketQueue( + const FieldTrialsView& field_trials, + Timestamp creation_time) { + if (field_trials.IsEnabled("WebRTC-Pacer-UsePrioritizedPacketQueue")) { + return std::make_unique(creation_time); + } + return std::make_unique(creation_time); } } // namespace @@ -94,26 +73,25 @@ const float PacingController::kDefaultPaceMultiplier = 2.5f; const TimeDelta PacingController::kPausedProcessInterval = kCongestedPacketInterval; const TimeDelta PacingController::kMinSleepTime = TimeDelta::Millis(1); +const TimeDelta PacingController::kMaxEarlyProbeProcessing = + TimeDelta::Millis(1); PacingController::PacingController(Clock* clock, PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const FieldTrialsView& field_trials, ProcessMode mode) : mode_(mode), clock_(clock), packet_sender_(packet_sender), - fallback_field_trials_( - !field_trials ? std::make_unique() : nullptr), - field_trials_(field_trials ? field_trials : fallback_field_trials_.get()), + field_trials_(field_trials), drain_large_queues_( - !IsDisabled(*field_trials_, "WebRTC-Pacer-DrainQueue")), + !IsDisabled(field_trials_, "WebRTC-Pacer-DrainQueue")), send_padding_if_silent_( - IsEnabled(*field_trials_, "WebRTC-Pacer-PadInSilence")), - pace_audio_(IsEnabled(*field_trials_, "WebRTC-Pacer-BlockAudio")), + IsEnabled(field_trials_, "WebRTC-Pacer-PadInSilence")), + pace_audio_(IsEnabled(field_trials_, "WebRTC-Pacer-BlockAudio")), ignore_transport_overhead_( - IsEnabled(*field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")), - padding_target_duration_(GetDynamicPaddingTarget(*field_trials_)), + IsEnabled(field_trials_, "WebRTC-Pacer-IgnoreTransportOverhead")), + padding_target_duration_(GetDynamicPaddingTarget(field_trials_)), min_packet_limit_(kDefaultMinPacketLimit), transport_overhead_per_packet_(DataSize::Zero()), last_timestamp_(clock_->CurrentTime()), @@ -124,16 +102,15 @@ PacingController::PacingController(Clock* clock, padding_debt_(DataSize::Zero()), media_rate_(DataRate::Zero()), padding_rate_(DataRate::Zero()), - prober_(*field_trials_), + prober_(field_trials_), probing_send_failure_(false), pacing_bitrate_(DataRate::Zero()), last_process_time_(clock->CurrentTime()), last_send_time_(last_process_time_), - packet_queue_(last_process_time_, field_trials_), - packet_counter_(0), - congestion_window_size_(DataSize::PlusInfinity()), - outstanding_data_(DataSize::Zero()), - queue_time_limit(kMaxExpectedQueueLength), + seen_first_packet_(false), + packet_queue_(CreatePacketQueue(field_trials_, last_process_time_)), + congested_(false), + queue_time_limit_(kMaxExpectedQueueLength), account_for_audio_(false), include_overhead_(false) { if (!drain_large_queues_) { @@ -142,7 +119,7 @@ PacingController::PacingController(Clock* clock, } FieldTrialParameter min_packet_limit_ms("", min_packet_limit_.ms()); ParseFieldTrial({&min_packet_limit_ms}, - field_trials_->Lookup("WebRTC-Pacer-MinPacketLimitMs")); + field_trials_.Lookup("WebRTC-Pacer-MinPacketLimitMs")); min_packet_limit_ = TimeDelta::Millis(min_packet_limit_ms.Get()); UpdateBudgetWithElapsedTime(min_packet_limit_); } @@ -157,43 +134,25 @@ void PacingController::Pause() { if (!paused_) RTC_LOG(LS_INFO) << "PacedSender paused."; paused_ = true; - packet_queue_.SetPauseState(true, CurrentTime()); + packet_queue_->SetPauseState(true, CurrentTime()); } void PacingController::Resume() { if (paused_) RTC_LOG(LS_INFO) << "PacedSender resumed."; paused_ = false; - packet_queue_.SetPauseState(false, CurrentTime()); + packet_queue_->SetPauseState(false, CurrentTime()); } bool PacingController::IsPaused() const { return paused_; } -void PacingController::SetCongestionWindow(DataSize congestion_window_size) { - const bool was_congested = Congested(); - congestion_window_size_ = congestion_window_size; - if (was_congested && !Congested()) { - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(CurrentTime()); - UpdateBudgetWithElapsedTime(elapsed_time); - } -} - -void PacingController::UpdateOutstandingData(DataSize outstanding_data) { - const bool was_congested = Congested(); - outstanding_data_ = outstanding_data; - if (was_congested && !Congested()) { - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(CurrentTime()); - UpdateBudgetWithElapsedTime(elapsed_time); - } -} - -bool PacingController::Congested() const { - if (congestion_window_size_.IsFinite()) { - return outstanding_data_ >= congestion_window_size_; +void PacingController::SetCongested(bool congested) { + if (congested_ && !congested) { + UpdateBudgetWithElapsedTime(UpdateTimeAndGetElapsed(CurrentTime())); } - return false; + congested_ = congested; } bool PacingController::IsProbing() const { @@ -214,16 +173,25 @@ Timestamp PacingController::CurrentTime() const { } void PacingController::SetProbingEnabled(bool enabled) { - RTC_CHECK_EQ(0, packet_counter_); + RTC_CHECK(!seen_first_packet_); prober_.SetEnabled(enabled); } void PacingController::SetPacingRates(DataRate pacing_rate, DataRate padding_rate) { - RTC_DCHECK_GT(pacing_rate, DataRate::Zero()); + static constexpr DataRate kMaxRate = DataRate::KilobitsPerSec(100'000); + RTC_CHECK_GT(pacing_rate, DataRate::Zero()); + RTC_CHECK_GE(padding_rate, DataRate::Zero()); + if (pacing_rate > kMaxRate || padding_rate > kMaxRate) { + RTC_LOG(LS_WARNING) << "Very high pacing rates ( > " << kMaxRate.kbps() + << " kbps) configured: pacing = " << pacing_rate.kbps() + << " kbps, padding = " << padding_rate.kbps() + << " kbps."; + } media_rate_ = pacing_rate; padding_rate_ = padding_rate; pacing_bitrate_ = pacing_rate; + media_budget_.set_target_rate_kbps(pacing_rate.kbps()); padding_budget_.set_target_rate_kbps(padding_rate.kbps()); RTC_LOG(LS_VERBOSE) << "bwe:pacer_updated pacing_kbps=" @@ -235,10 +203,25 @@ void PacingController::EnqueuePacket(std::unique_ptr packet) { RTC_DCHECK(pacing_bitrate_ > DataRate::Zero()) << "SetPacingRate must be called before InsertPacket."; RTC_CHECK(packet->packet_type()); - // Get priority first and store in temporary, to avoid chance of object being - // moved before GetPriorityForType() being called. - const int priority = GetPriorityForType(*packet->packet_type()); - EnqueuePacketInternal(std::move(packet), priority); + + prober_.OnIncomingPacket(DataSize::Bytes(packet->payload_size())); + + Timestamp now = CurrentTime(); + if (mode_ == ProcessMode::kDynamic && packet_queue_->Empty()) { + // If queue is empty, we need to "fast-forward" the last process time, + // so that we don't use passed time as budget for sending the first new + // packet. + Timestamp target_process_time = now; + Timestamp next_send_time = NextSendTime(); + if (next_send_time.IsFinite()) { + // There was already a valid planned send time, such as a keep-alive. + // Use that as last process time only if it's prior to now. + target_process_time = std::min(now, next_send_time); + } + UpdateBudgetWithElapsedTime(UpdateTimeAndGetElapsed(target_process_time)); + } + packet_queue_->Push(now, std::move(packet)); + seen_first_packet_ = true; } void PacingController::SetAccountForAudioPackets(bool account_for_audio) { @@ -247,14 +230,12 @@ void PacingController::SetAccountForAudioPackets(bool account_for_audio) { void PacingController::SetIncludeOverhead() { include_overhead_ = true; - packet_queue_.SetIncludeOverhead(); } void PacingController::SetTransportOverhead(DataSize overhead_per_packet) { if (ignore_transport_overhead_) return; transport_overhead_per_packet_ = overhead_per_packet; - packet_queue_.SetTransportOverhead(overhead_per_packet); } TimeDelta PacingController::ExpectedQueueTime() const { @@ -265,11 +246,16 @@ TimeDelta PacingController::ExpectedQueueTime() const { } size_t PacingController::QueueSizePackets() const { - return packet_queue_.SizeInPackets(); + return rtc::checked_cast(packet_queue_->SizeInPackets()); } DataSize PacingController::QueueSizeData() const { - return packet_queue_.Size(); + DataSize size = packet_queue_->SizeInPayloadBytes(); + if (include_overhead_) { + size += static_cast(packet_queue_->SizeInPackets()) * + transport_overhead_per_packet_; + } + return size; } DataSize PacingController::CurrentBufferLevel() const { @@ -281,33 +267,7 @@ absl::optional PacingController::FirstSentPacketTime() const { } Timestamp PacingController::OldestPacketEnqueueTime() const { - return packet_queue_.OldestEnqueueTime(); -} - -void PacingController::EnqueuePacketInternal( - std::unique_ptr packet, - int priority) { - prober_.OnIncomingPacket(DataSize::Bytes(packet->payload_size())); - - Timestamp now = CurrentTime(); - - if (mode_ == ProcessMode::kDynamic && packet_queue_.Empty()) { - // If queue is empty, we need to "fast-forward" the last process time, - // so that we don't use passed time as budget for sending the first new - // packet. - Timestamp target_process_time = now; - Timestamp next_send_time = NextSendTime(); - if (next_send_time.IsFinite()) { - // There was already a valid planned send time, such as a keep-alive. - // Use that as last process time only if it's prior to now. - target_process_time = std::min(now, next_send_time); - } - - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(target_process_time); - UpdateBudgetWithElapsedTime(elapsed_time); - last_process_time_ = target_process_time; - } - packet_queue_.Push(priority, now, packet_counter_++, std::move(packet)); + return packet_queue_->OldestEnqueueTime(); } TimeDelta PacingController::UpdateTimeAndGetElapsed(Timestamp now) { @@ -316,7 +276,6 @@ TimeDelta PacingController::UpdateTimeAndGetElapsed(Timestamp now) { if (last_process_time_.IsMinusInfinity() || now < last_process_time_) { return TimeDelta::Zero(); } - RTC_DCHECK_GE(now, last_process_time_); TimeDelta elapsed_time = now - last_process_time_; last_process_time_ = now; if (elapsed_time > kMaxElapsedTime) { @@ -329,12 +288,10 @@ TimeDelta PacingController::UpdateTimeAndGetElapsed(Timestamp now) { } bool PacingController::ShouldSendKeepalive(Timestamp now) const { - if (send_padding_if_silent_ || paused_ || Congested() || - packet_counter_ == 0) { + if (send_padding_if_silent_ || paused_ || congested_ || !seen_first_packet_) { // We send a padding packet every 500 ms to ensure we won't get stuck in // congested state due to no feedback being received. - TimeDelta elapsed_since_last_send = now - last_send_time_; - if (elapsed_since_last_send >= kCongestedPacketInterval) { + if (now - last_send_time_ >= kCongestedPacketInterval) { return true; } } @@ -343,17 +300,17 @@ bool PacingController::ShouldSendKeepalive(Timestamp now) const { Timestamp PacingController::NextSendTime() const { const Timestamp now = CurrentTime(); + Timestamp next_send_time = Timestamp::PlusInfinity(); if (paused_) { return last_send_time_ + kPausedProcessInterval; } // If probing is active, that always takes priority. - if (prober_.is_probing()) { + if (prober_.is_probing() && !probing_send_failure_) { Timestamp probe_time = prober_.NextProbeTime(now); - // `probe_time` == PlusInfinity indicates no probe scheduled. - if (probe_time != Timestamp::PlusInfinity() && !probing_send_failure_) { - return probe_time; + if (!probe_time.IsPlusInfinity()) { + return probe_time.IsMinusInfinity() ? now : probe_time; } } @@ -365,86 +322,60 @@ Timestamp PacingController::NextSendTime() const { // In dynamic mode, figure out when the next packet should be sent, // given the current conditions. - if (!pace_audio_) { - // Not pacing audio, if leading packet is audio its target send - // time is the time at which it was enqueued. - absl::optional audio_enqueue_time = - packet_queue_.LeadingAudioPacketEnqueueTime(); - if (audio_enqueue_time.has_value()) { - return *audio_enqueue_time; - } + // Not pacing audio, if leading packet is audio its target send + // time is the time at which it was enqueued. + Timestamp unpaced_audio_time = + pace_audio_ ? Timestamp::PlusInfinity() + : packet_queue_->LeadingAudioPacketEnqueueTime(); + if (unpaced_audio_time.IsFinite()) { + return unpaced_audio_time; } - if (Congested() || packet_counter_ == 0) { + if (congested_ || !seen_first_packet_) { // We need to at least send keep-alive packets with some interval. return last_send_time_ + kCongestedPacketInterval; } - // Check how long until we can send the next media packet. - if (media_rate_ > DataRate::Zero() && !packet_queue_.Empty()) { - return std::min(last_send_time_ + kPausedProcessInterval, - last_process_time_ + media_debt_ / media_rate_); - } - - // If we _don't_ have pending packets, check how long until we have - // bandwidth for padding packets. Both media and padding debts must - // have been drained to do this. - if (padding_rate_ > DataRate::Zero() && packet_queue_.Empty()) { + if (media_rate_ > DataRate::Zero() && !packet_queue_->Empty()) { + // Check how long until we can send the next media packet. + next_send_time = last_process_time_ + media_debt_ / media_rate_; + } else if (padding_rate_ > DataRate::Zero() && packet_queue_->Empty()) { + // If we _don't_ have pending packets, check how long until we have + // bandwidth for padding packets. Both media and padding debts must + // have been drained to do this. + RTC_DCHECK_GT(media_rate_, DataRate::Zero()); TimeDelta drain_time = std::max(media_debt_ / media_rate_, padding_debt_ / padding_rate_); - return std::min(last_send_time_ + kPausedProcessInterval, - last_process_time_ + drain_time); + + if (drain_time.IsZero() && + (!media_debt_.IsZero() || !padding_debt_.IsZero())) { + // We have a non-zero debt, but drain time is smaller than tick size of + // TimeDelta, round it up to the smallest possible non-zero delta. + drain_time = TimeDelta::Micros(1); + } + next_send_time = last_process_time_ + drain_time; + } else { + // Nothing to do. + next_send_time = last_process_time_ + kPausedProcessInterval; } if (send_padding_if_silent_) { - return last_send_time_ + kPausedProcessInterval; + next_send_time = + std::min(next_send_time, last_send_time_ + kPausedProcessInterval); } - return last_process_time_ + kPausedProcessInterval; + + return next_send_time; } void PacingController::ProcessPackets() { Timestamp now = CurrentTime(); Timestamp target_send_time = now; - if (mode_ == ProcessMode::kDynamic) { - target_send_time = NextSendTime(); - TimeDelta early_execute_margin = - prober_.is_probing() ? kMaxEarlyProbeProcessing : TimeDelta::Zero(); - if (target_send_time.IsMinusInfinity()) { - target_send_time = now; - } else if (now < target_send_time - early_execute_margin) { - // We are too early, but if queue is empty still allow draining some debt. - // Probing is allowed to be sent up to kMinSleepTime early. - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(now); - UpdateBudgetWithElapsedTime(elapsed_time); - return; - } - - if (target_send_time < last_process_time_) { - // After the last process call, at time X, the target send time - // shifted to be earlier than X. This should normally not happen - // but we want to make sure rounding errors or erratic behavior - // of NextSendTime() does not cause issue. In particular, if the - // buffer reduction of - // rate * (target_send_time - previous_process_time) - // in the main loop doesn't clean up the existing debt we may not - // be able to send again. We don't want to check this reordering - // there as it is the normal exit condtion when the buffer is - // exhausted and there are packets in the queue. - UpdateBudgetWithElapsedTime(last_process_time_ - target_send_time); - target_send_time = last_process_time_; - } - } - - Timestamp previous_process_time = last_process_time_; - TimeDelta elapsed_time = UpdateTimeAndGetElapsed(now); if (ShouldSendKeepalive(now)) { + DataSize keepalive_data_sent = DataSize::Zero(); // We can not send padding unless a normal packet has first been sent. If // we do, timestamps get messed up. - if (packet_counter_ == 0) { - last_send_time_ = now; - } else { - DataSize keepalive_data_sent = DataSize::Zero(); + if (seen_first_packet_) { std::vector> keepalive_packets = packet_sender_->GeneratePadding(DataSize::Bytes(1)); for (auto& packet : keepalive_packets) { @@ -455,26 +386,41 @@ void PacingController::ProcessPackets() { EnqueuePacket(std::move(packet)); } } - OnPaddingSent(keepalive_data_sent); } + OnPacketSent(RtpPacketMediaType::kPadding, keepalive_data_sent, now); } if (paused_) { return; } + if (mode_ == ProcessMode::kDynamic) { + TimeDelta early_execute_margin = + prober_.is_probing() ? kMaxEarlyProbeProcessing : TimeDelta::Zero(); + + target_send_time = NextSendTime(); + if (now + early_execute_margin < target_send_time) { + // We are too early, but if queue is empty still allow draining some debt. + // Probing is allowed to be sent up to kMinSleepTime early. + UpdateBudgetWithElapsedTime(UpdateTimeAndGetElapsed(now)); + return; + } + } + + TimeDelta elapsed_time = UpdateTimeAndGetElapsed(target_send_time); + if (elapsed_time > TimeDelta::Zero()) { DataRate target_rate = pacing_bitrate_; - DataSize queue_size_data = packet_queue_.Size(); + DataSize queue_size_data = QueueSizeData(); if (queue_size_data > DataSize::Zero()) { // Assuming equal size packets and input/output rate, the average packet // has avg_time_left_ms left to get queue_size_bytes out of the queue, if // time constraint shall be met. Determine bitrate needed for that. - packet_queue_.UpdateQueueTime(now); + packet_queue_->UpdateAverageQueueTime(now); if (drain_large_queues_) { TimeDelta avg_time_left = std::max(TimeDelta::Millis(1), - queue_time_limit - packet_queue_.AverageQueueTime()); + queue_time_limit_ - packet_queue_->AverageQueueTime()); DataRate min_rate_needed = queue_size_data / avg_time_left; if (min_rate_needed > target_rate) { target_rate = min_rate_needed; @@ -489,13 +435,12 @@ void PacingController::ProcessPackets() { // up to (process interval duration) * (target rate), so we only need to // update it once before the packet sending loop. media_budget_.set_target_rate_kbps(target_rate.kbps()); - UpdateBudgetWithElapsedTime(elapsed_time); } else { media_rate_ = target_rate; } + UpdateBudgetWithElapsedTime(elapsed_time); } - bool first_packet_in_probe = false; PacedPacketInfo pacing_info; DataSize recommended_probe_size = DataSize::Zero(); bool is_probing = prober_.is_probing(); @@ -504,7 +449,6 @@ void PacingController::ProcessPackets() { // use actual send time rather than target. pacing_info = prober_.CurrentCluster(now).value_or(PacedPacketInfo()); if (pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { - first_packet_in_probe = pacing_info.probe_cluster_bytes_sent == 0; recommended_probe_size = prober_.RecommendedMinProbeSize(); RTC_DCHECK_GT(recommended_probe_size, DataSize::Zero()); } else { @@ -514,101 +458,96 @@ void PacingController::ProcessPackets() { } DataSize data_sent = DataSize::Zero(); - - // The paused state is checked in the loop since it leaves the critical - // section allowing the paused state to be changed from other code. - while (!paused_) { - if (first_packet_in_probe) { - // If first packet in probe, insert a small padding packet so we have a - // more reliable start window for the rate estimation. - auto padding = packet_sender_->GeneratePadding(DataSize::Bytes(1)); - // If no RTP modules sending media are registered, we may not get a - // padding packet back. - if (!padding.empty()) { - // Insert with high priority so larger media packets don't preempt it. - EnqueuePacketInternal(std::move(padding[0]), kFirstPriority); - // We should never get more than one padding packets with a requested - // size of 1 byte. - RTC_DCHECK_EQ(padding.size(), 1u); - } - first_packet_in_probe = false; - } - - if (mode_ == ProcessMode::kDynamic && - previous_process_time < target_send_time) { - // Reduce buffer levels with amount corresponding to time between last - // process and target send time for the next packet. - // If the process call is late, that may be the time between the optimal - // send times for two packets we should already have sent. - UpdateBudgetWithElapsedTime(target_send_time - previous_process_time); - previous_process_time = target_send_time; - } - - // Fetch the next packet, so long as queue is not empty or budget is not + // Circuit breaker, making sure main loop isn't forever. + static constexpr int kMaxIterations = 1 << 16; + int iteration = 0; + int packets_sent = 0; + int padding_packets_generated = 0; + for (; iteration < kMaxIterations; ++iteration) { + // Fetch packet, so long as queue is not empty or budget is not // exhausted. std::unique_ptr rtp_packet = GetPendingPacket(pacing_info, target_send_time, now); - if (rtp_packet == nullptr) { // No packet available to send, check if we should send padding. DataSize padding_to_add = PaddingToAdd(recommended_probe_size, data_sent); if (padding_to_add > DataSize::Zero()) { std::vector> padding_packets = packet_sender_->GeneratePadding(padding_to_add); - if (padding_packets.empty()) { - // No padding packets were generated, quite send loop. - break; - } - for (auto& packet : padding_packets) { - EnqueuePacket(std::move(packet)); + if (!padding_packets.empty()) { + padding_packets_generated += padding_packets.size(); + for (auto& packet : padding_packets) { + EnqueuePacket(std::move(packet)); + } + // Continue loop to send the padding that was just added. + continue; + } else { + // Can't generate padding, still update padding budget for next send + // time. + UpdatePaddingBudgetWithSentData(padding_to_add); } - // Continue loop to send the padding that was just added. - continue; } - // Can't fetch new packet and no padding to send, exit send loop. break; - } - - RTC_DCHECK(rtp_packet); - RTC_DCHECK(rtp_packet->packet_type().has_value()); - const RtpPacketMediaType packet_type = *rtp_packet->packet_type(); - DataSize packet_size = DataSize::Bytes(rtp_packet->payload_size() + - rtp_packet->padding_size()); - - if (include_overhead_) { - packet_size += DataSize::Bytes(rtp_packet->headers_size()) + - transport_overhead_per_packet_; - } + } else { + RTC_DCHECK(rtp_packet); + RTC_DCHECK(rtp_packet->packet_type().has_value()); + const RtpPacketMediaType packet_type = *rtp_packet->packet_type(); + DataSize packet_size = DataSize::Bytes(rtp_packet->payload_size() + + rtp_packet->padding_size()); + + if (include_overhead_) { + packet_size += DataSize::Bytes(rtp_packet->headers_size()) + + transport_overhead_per_packet_; + } - packet_sender_->SendPacket(std::move(rtp_packet), pacing_info); - for (auto& packet : packet_sender_->FetchFec()) { - EnqueuePacket(std::move(packet)); - } - data_sent += packet_size; + packet_sender_->SendPacket(std::move(rtp_packet), pacing_info); + for (auto& packet : packet_sender_->FetchFec()) { + EnqueuePacket(std::move(packet)); + } + data_sent += packet_size; + ++packets_sent; - // Send done, update send/process time to the target send time. - OnPacketSent(packet_type, packet_size, target_send_time); + // Send done, update send time. + OnPacketSent(packet_type, packet_size, now); - // If we are currently probing, we need to stop the send loop when we have - // reached the send target. - if (is_probing && data_sent >= recommended_probe_size) { - break; - } + if (is_probing) { + pacing_info.probe_cluster_bytes_sent += packet_size.bytes(); + // If we are currently probing, we need to stop the send loop when we + // have reached the send target. + if (data_sent >= recommended_probe_size) { + break; + } + } - if (mode_ == ProcessMode::kDynamic) { // Update target send time in case that are more packets that we are late // in processing. - Timestamp next_send_time = NextSendTime(); - if (next_send_time.IsMinusInfinity()) { - target_send_time = now; - } else { - target_send_time = std::min(now, next_send_time); + if (mode_ == ProcessMode::kDynamic) { + target_send_time = NextSendTime(); + if (target_send_time > now) { + // Exit loop if not probing. + if (!is_probing) { + break; + } + target_send_time = now; + } + UpdateBudgetWithElapsedTime(UpdateTimeAndGetElapsed(target_send_time)); } } } - last_process_time_ = std::max(last_process_time_, previous_process_time); + if (iteration >= kMaxIterations) { + // Circuit break activated. Log warning, adjust send time and return. + // TODO(sprang): Consider completely clearing state. + RTC_LOG(LS_ERROR) << "PacingController exceeded max iterations in " + "send-loop: packets sent = " + << packets_sent << ", padding packets generated = " + << padding_packets_generated + << ", bytes sent = " << data_sent.bytes(); + last_send_time_ = now; + last_process_time_ = now; + return; + } if (is_probing) { probing_send_failure_ = data_sent == DataSize::Zero(); @@ -620,19 +559,19 @@ void PacingController::ProcessPackets() { DataSize PacingController::PaddingToAdd(DataSize recommended_probe_size, DataSize data_sent) const { - if (!packet_queue_.Empty()) { + if (!packet_queue_->Empty()) { // Actual payload available, no need to add padding. return DataSize::Zero(); } - if (Congested()) { + if (congested_) { // Don't add padding if congested, even if requested for probing. return DataSize::Zero(); } - if (packet_counter_ == 0) { - // We can not send padding unless a normal packet has first been sent. If we - // do, timestamps get messed up. + if (!seen_first_packet_) { + // We can not send padding unless a normal packet has first been sent. If + // we do, timestamps get messed up. return DataSize::Zero(); } @@ -656,7 +595,23 @@ std::unique_ptr PacingController::GetPendingPacket( const PacedPacketInfo& pacing_info, Timestamp target_send_time, Timestamp now) { - if (packet_queue_.Empty()) { + const bool is_probe = + pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe; + // If first packet in probe, insert a small padding packet so we have a + // more reliable start window for the rate estimation. + if (is_probe && pacing_info.probe_cluster_bytes_sent == 0) { + auto padding = packet_sender_->GeneratePadding(DataSize::Bytes(1)); + // If no RTP modules sending media are registered, we may not get a + // padding packet back. + if (!padding.empty()) { + // We should never get more than one padding packets with a requested + // size of 1 byte. + RTC_DCHECK_EQ(padding.size(), 1u); + return std::move(padding[0]); + } + } + + if (packet_queue_->Empty()) { return nullptr; } @@ -664,10 +619,9 @@ std::unique_ptr PacingController::GetPendingPacket( // Unpaced audio packets and probes are exempted from send checks. bool unpaced_audio_packet = - !pace_audio_ && packet_queue_.LeadingAudioPacketEnqueueTime().has_value(); - bool is_probe = pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe; + !pace_audio_ && packet_queue_->LeadingAudioPacketEnqueueTime().IsFinite(); if (!unpaced_audio_packet && !is_probe) { - if (Congested()) { + if (congested_) { // Don't send anything if congested. return nullptr; } @@ -691,31 +645,22 @@ std::unique_ptr PacingController::GetPendingPacket( } } - return packet_queue_.Pop(); + return packet_queue_->Pop(); } void PacingController::OnPacketSent(RtpPacketMediaType packet_type, DataSize packet_size, Timestamp send_time) { - if (!first_sent_packet_time_) { + if (!first_sent_packet_time_ && packet_type != RtpPacketMediaType::kPadding) { first_sent_packet_time_ = send_time; } + bool audio_packet = packet_type == RtpPacketMediaType::kAudio; - if (!audio_packet || account_for_audio_) { - // Update media bytes sent. + if ((!audio_packet || account_for_audio_) && packet_size > DataSize::Zero()) { UpdateBudgetWithSentData(packet_size); } - last_send_time_ = send_time; - last_process_time_ = send_time; -} -void PacingController::OnPaddingSent(DataSize data_sent) { - if (data_sent > DataSize::Zero()) { - UpdateBudgetWithSentData(data_sent); - } - Timestamp now = CurrentTime(); - last_send_time_ = now; - last_process_time_ = now; + last_send_time_ = send_time; } void PacingController::UpdateBudgetWithElapsedTime(TimeDelta delta) { @@ -730,20 +675,26 @@ void PacingController::UpdateBudgetWithElapsedTime(TimeDelta delta) { } void PacingController::UpdateBudgetWithSentData(DataSize size) { - outstanding_data_ += size; if (mode_ == ProcessMode::kPeriodic) { media_budget_.UseBudget(size.bytes()); - padding_budget_.UseBudget(size.bytes()); } else { media_debt_ += size; media_debt_ = std::min(media_debt_, media_rate_ * kMaxDebtInTime); + } + UpdatePaddingBudgetWithSentData(size); +} + +void PacingController::UpdatePaddingBudgetWithSentData(DataSize size) { + if (mode_ == ProcessMode::kPeriodic) { + padding_budget_.UseBudget(size.bytes()); + } else { padding_debt_ += size; padding_debt_ = std::min(padding_debt_, padding_rate_ * kMaxDebtInTime); } } void PacingController::SetQueueTimeLimit(TimeDelta limit) { - queue_time_limit = limit; + queue_time_limit_ = limit; } } // namespace webrtc diff --git a/modules/pacing/pacing_controller.h b/modules/pacing/pacing_controller.h index 5d6d26b917..687b51df4a 100644 --- a/modules/pacing/pacing_controller.h +++ b/modules/pacing/pacing_controller.h @@ -19,14 +19,12 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/function_view.h" -#include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/field_trial_based_config.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "modules/pacing/bitrate_prober.h" #include "modules/pacing/interval_budget.h" -#include "modules/pacing/round_robin_packet_queue.h" #include "modules/pacing/rtp_packet_pacer.h" #include "modules/rtp_rtcp/include/rtp_packet_sender.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" @@ -61,6 +59,47 @@ class PacingController { DataSize size) = 0; }; + // Interface for class hanlding storage of and prioritization of packets + // pending to be sent by the pacer. + // Note that for the methods taking a Timestamp as parameter, the parameter + // will never decrease between two subsequent calls. + class PacketQueue { + public: + virtual ~PacketQueue() = default; + + virtual void Push(Timestamp enqueue_time, + std::unique_ptr packet) = 0; + virtual std::unique_ptr Pop() = 0; + + virtual int SizeInPackets() const = 0; + bool Empty() const { return SizeInPackets() == 0; } + virtual DataSize SizeInPayloadBytes() const = 0; + + // If the next packet, that would be returned by Pop() if called + // now, is an audio packet this method returns the enqueue time + // of that packet. If queue is empty or top packet is not audio, + // returns Timestamp::MinusInfinity(). + virtual Timestamp LeadingAudioPacketEnqueueTime() const = 0; + + // Enqueue time of the oldest packet in the queue, + // Timestamp::MinusInfinity() if queue is empty. + virtual Timestamp OldestEnqueueTime() const = 0; + + // Average queue time for the packets currently in the queue. + // The queuing time is calculated from Push() to the last UpdateQueueTime() + // call - with any time spent in a paused state subtracted. + // Returns TimeDelta::Zero() for an empty queue. + virtual TimeDelta AverageQueueTime() const = 0; + + // Called during packet processing or when pause stats changes. Since the + // AverageQueueTime() method does not look at the wall time, this method + // needs to be called before querying queue time. + virtual void UpdateAverageQueueTime(Timestamp now) = 0; + + // Set the pause state, while `paused` is true queuing time is not counted. + virtual void SetPauseState(bool paused, Timestamp now) = 0; + }; + // Expected max pacer delay. If ExpectedQueueTime() is higher than // this value, the packet producers should wait (eg drop frames rather than // encoding them). Bitrate sent may temporarily exceed target set by @@ -79,10 +118,14 @@ class PacingController { static const TimeDelta kMinSleepTime; + // Allow probes to be processed slightly ahead of inteded send time. Currently + // set to 1ms as this is intended to allow times be rounded down to the + // nearest millisecond. + static const TimeDelta kMaxEarlyProbeProcessing; + PacingController(Clock* clock, PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const FieldTrialsView& field_trials, ProcessMode mode); ~PacingController(); @@ -97,8 +140,7 @@ class PacingController { void Resume(); // Resume sending packets. bool IsPaused() const; - void SetCongestionWindow(DataSize congestion_window_size); - void UpdateOutstandingData(DataSize outstanding_data); + void SetCongested(bool congested); // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate); @@ -145,19 +187,16 @@ class PacingController { // is available. void ProcessPackets(); - bool Congested() const; - bool IsProbing() const; private: - void EnqueuePacketInternal(std::unique_ptr packet, - int priority); TimeDelta UpdateTimeAndGetElapsed(Timestamp now); bool ShouldSendKeepalive(Timestamp now) const; // Updates the number of bytes that can be sent for the next time interval. void UpdateBudgetWithElapsedTime(TimeDelta delta); void UpdateBudgetWithSentData(DataSize size); + void UpdatePaddingBudgetWithSentData(DataSize size); DataSize PaddingToAdd(DataSize recommended_probe_size, DataSize data_sent) const; @@ -169,15 +208,13 @@ class PacingController { void OnPacketSent(RtpPacketMediaType packet_type, DataSize packet_size, Timestamp send_time); - void OnPaddingSent(DataSize padding_sent); Timestamp CurrentTime() const; const ProcessMode mode_; Clock* const clock_; PacketSender* const packet_sender_; - const std::unique_ptr fallback_field_trials_; - const WebRtcKeyValueConfig* field_trials_; + const FieldTrialsView& field_trials_; const bool drain_large_queues_; const bool send_padding_if_silent_; @@ -196,9 +233,9 @@ class PacingController { mutable Timestamp last_timestamp_; bool paused_; - // In dynamic mode, `media_budget_` and `padding_budget_` will be used to + // In periodic mode, `media_budget_` and `padding_budget_` will be used to // track when packets can be sent. - // In periodic mode, `media_debt_` and `padding_debt_` will be used together + // In dynamic mode, `media_debt_` and `padding_debt_` will be used together // with the target rates. // This is the media budget, keeping track of how many bits of media @@ -222,14 +259,13 @@ class PacingController { Timestamp last_process_time_; Timestamp last_send_time_; absl::optional first_sent_packet_time_; + bool seen_first_packet_; - RoundRobinPacketQueue packet_queue_; - uint64_t packet_counter_; + std::unique_ptr packet_queue_; - DataSize congestion_window_size_; - DataSize outstanding_data_; + bool congested_; - TimeDelta queue_time_limit; + TimeDelta queue_time_limit_; bool account_for_audio_; bool include_overhead_; }; diff --git a/modules/pacing/pacing_controller_unittest.cc b/modules/pacing/pacing_controller_unittest.cc index e7634cd8d5..27f0c46525 100644 --- a/modules/pacing/pacing_controller_unittest.cc +++ b/modules/pacing/pacing_controller_unittest.cc @@ -21,7 +21,6 @@ #include "modules/pacing/packet_router.h" #include "system_wrappers/include/clock.h" #include "test/explicit_key_value_config.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" @@ -30,6 +29,7 @@ using ::testing::Field; using ::testing::Pointee; using ::testing::Property; using ::testing::Return; +using ::testing::WithoutArgs; namespace webrtc { namespace test { @@ -60,7 +60,7 @@ std::unique_ptr BuildPacket(RtpPacketMediaType type, packet->set_packet_type(type); packet->SetSsrc(ssrc); packet->SetSequenceNumber(sequence_number); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); packet->SetPayloadSize(size); return packet; } @@ -73,7 +73,7 @@ class MockPacingControllerCallback : public PacingController::PacketSender { void SendPacket(std::unique_ptr packet, const PacedPacketInfo& cluster_info) override { SendPacket(packet->Ssrc(), packet->SequenceNumber(), - packet->capture_time_ms(), + packet->capture_time().ms(), packet->packet_type() == RtpPacketMediaType::kRetransmission, packet->packet_type() == RtpPacketMediaType::kPadding); } @@ -209,13 +209,13 @@ class PacingControllerProbing : public PacingController::PacketSender { class PacingControllerTest : public ::testing::TestWithParam { protected: - PacingControllerTest() : clock_(123456) {} + PacingControllerTest() : clock_(123456), trials_("") {} void SetUp() override { srand(0); // Need to initialize PacingController after we initialize clock. - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + GetParam()); Init(); } @@ -320,6 +320,7 @@ class PacingControllerTest SimulatedClock clock_; ::testing::NiceMock callback_; + ExplicitKeyValueConfig trials_; std::unique_ptr pacer_; }; @@ -364,7 +365,8 @@ class PacingControllerFieldTrialTest }; TEST_P(PacingControllerFieldTrialTest, DefaultNoPaddingInSilence) { - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); @@ -378,8 +380,9 @@ TEST_P(PacingControllerFieldTrialTest, DefaultNoPaddingInSilence) { } TEST_P(PacingControllerFieldTrialTest, PaddingInSilenceWithTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-PadInSilence/Enabled/"); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials( + "WebRTC-Pacer-PadInSilence/Enabled/"); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(kTargetRate, DataRate::Zero()); // Video packet to reset last send time and provide padding data. InsertPacket(&pacer, &video); @@ -393,16 +396,15 @@ TEST_P(PacingControllerFieldTrialTest, PaddingInSilenceWithTrial) { } TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); + const test::ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/"); EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::KilobitsPerSec(10000), DataRate::Zero()); - pacer.SetCongestionWindow(DataSize::Bytes(video.packet_size - 100)); - pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); + pacer.SetCongested(true); // Audio packet blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(0); @@ -414,7 +416,7 @@ TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { ProcessNext(&pacer); // Audio packet unblocked when congestion window clear. ::testing::Mock::VerifyAndClearExpectations(&callback_); - pacer.UpdateOutstandingData(DataSize::Zero()); + pacer.SetCongested(false); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); } @@ -422,14 +424,14 @@ TEST_P(PacingControllerFieldTrialTest, CongestionWindowAffectsAudioInTrial) { TEST_P(PacingControllerFieldTrialTest, DefaultCongestionWindowDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(10000000), DataRate::Zero()); - pacer.SetCongestionWindow(DataSize::Bytes(800)); - pacer.UpdateOutstandingData(DataSize::Zero()); // Video packet fills congestion window. InsertPacket(&pacer, &video); EXPECT_CALL(callback_, SendPacket).Times(1); ProcessNext(&pacer); + pacer.SetCongested(true); // Audio not blocked due to congestion. InsertPacket(&pacer, &audio); EXPECT_CALL(callback_, SendPacket).Times(1); @@ -437,8 +439,8 @@ TEST_P(PacingControllerFieldTrialTest, } TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { - ScopedFieldTrials trial("WebRTC-Pacer-BlockAudio/Enabled/"); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + ExplicitKeyValueConfig trials("WebRTC-Pacer-BlockAudio/Enabled/"); + PacingController pacer(&clock_, &callback_, trials, GetParam()); DataRate pacing_rate = DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond); pacer.SetPacingRates(pacing_rate, DataRate::Zero()); @@ -450,10 +452,9 @@ TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { InsertPacket(&pacer, &audio); Timestamp wait_start_time = clock_.CurrentTime(); Timestamp wait_end_time = Timestamp::MinusInfinity(); - EXPECT_CALL(callback_, SendPacket) - .WillOnce([&](uint32_t ssrc, uint16_t sequence_number, - int64_t capture_timestamp, bool retransmission, - bool padding) { wait_end_time = clock_.CurrentTime(); }); + EXPECT_CALL(callback_, SendPacket).WillOnce(WithoutArgs([&]() { + wait_end_time = clock_.CurrentTime(); + })); while (!wait_end_time.IsFinite()) { ProcessNext(&pacer); } @@ -468,7 +469,8 @@ TEST_P(PacingControllerFieldTrialTest, BudgetAffectsAudioInTrial) { TEST_P(PacingControllerFieldTrialTest, DefaultBudgetDoesNotAffectAudio) { EXPECT_CALL(callback_, SendPadding).Times(0); - PacingController pacer(&clock_, &callback_, nullptr, nullptr, GetParam()); + const test::ExplicitKeyValueConfig trials(""); + PacingController pacer(&clock_, &callback_, trials, GetParam()); pacer.SetPacingRates(DataRate::BitsPerSec(video.packet_size / 3 * 8 * kProcessIntervalsPerSecond), DataRate::Zero()); @@ -866,8 +868,8 @@ TEST_P(PacingControllerTest, VerifyAverageBitrateVaryingMediaPayload) { const int kTimeStep = 5; const TimeDelta kAveragingWindowLength = TimeDelta::Seconds(10); PacingControllerPadding callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); @@ -1058,21 +1060,18 @@ TEST_P(PacingControllerTest, SendsOnlyPaddingWhenCongested) { uint32_t ssrc = 202020; uint16_t sequence_number = 1000; int kPacketSize = 250; - int kCongestionWindow = kPacketSize * 10; - pacer_->UpdateOutstandingData(DataSize::Zero()); - pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); - int sent_data = 0; - while (sent_data < kCongestionWindow) { - sent_data += kPacketSize; - SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - AdvanceTimeAndProcess(); - } + // Send an initial packet so we have a last send time. + SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, + clock_.TimeInMilliseconds(), kPacketSize); + AdvanceTimeAndProcess(); ::testing::Mock::VerifyAndClearExpectations(&callback_); + + // Set congested state, we should not send anything until the 500ms since + // last send time limit for keep-alives is triggered. EXPECT_CALL(callback_, SendPacket).Times(0); EXPECT_CALL(callback_, SendPadding).Times(0); - + pacer_->SetCongested(true); size_t blocked_packets = 0; int64_t expected_time_until_padding = 500; while (expected_time_until_padding > 5) { @@ -1083,6 +1082,7 @@ TEST_P(PacingControllerTest, SendsOnlyPaddingWhenCongested) { pacer_->ProcessPackets(); expected_time_until_padding -= 5; } + ::testing::Mock::VerifyAndClearExpectations(&callback_); EXPECT_CALL(callback_, SendPadding(1)).WillOnce(Return(1)); EXPECT_CALL(callback_, SendPacket(_, _, _, _, true)).Times(1); @@ -1101,15 +1101,13 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { // to be sent in a row. pacer_->SetPacingRates(DataRate::BitsPerSec(400 * 8 * 1000 / 5), DataRate::Zero()); - // The congestion window is small enough to only let one packet through. - pacer_->SetCongestionWindow(DataSize::Bytes(800)); - pacer_->UpdateOutstandingData(DataSize::Zero()); // Not yet budget limited or congested, packet is sent. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); pacer_->ProcessPackets(); // Packet blocked due to congestion. + pacer_->SetCongested(true); Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(0); clock_.AdvanceTimeMilliseconds(5); @@ -1123,7 +1121,7 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); EXPECT_CALL(callback_, SendPacket).Times(1); clock_.AdvanceTimeMilliseconds(5); - pacer_->UpdateOutstandingData(DataSize::Zero()); + pacer_->SetCongested(false); pacer_->ProcessPackets(); // Should be blocked due to budget limitation as congestion has be removed. Send(RtpPacketMediaType::kVideo, ssrc, seq_num++, now_ms(), size); @@ -1132,61 +1130,6 @@ TEST_P(PacingControllerTest, DoesNotAllowOveruseAfterCongestion) { pacer_->ProcessPackets(); } -TEST_P(PacingControllerTest, ResumesSendingWhenCongestionEnds) { - uint32_t ssrc = 202020; - uint16_t sequence_number = 1000; - int64_t kPacketSize = 250; - int64_t kCongestionCount = 10; - int64_t kCongestionWindow = kPacketSize * kCongestionCount; - int64_t kCongestionTimeMs = 1000; - - pacer_->UpdateOutstandingData(DataSize::Zero()); - pacer_->SetCongestionWindow(DataSize::Bytes(kCongestionWindow)); - int sent_data = 0; - while (sent_data < kCongestionWindow) { - sent_data += kPacketSize; - SendAndExpectPacket(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - ::testing::Mock::VerifyAndClearExpectations(&callback_); - EXPECT_CALL(callback_, SendPacket).Times(0); - int unacked_packets = 0; - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - Send(RtpPacketMediaType::kVideo, ssrc, sequence_number++, - clock_.TimeInMilliseconds(), kPacketSize); - unacked_packets++; - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - ::testing::Mock::VerifyAndClearExpectations(&callback_); - - // First mark half of the congested packets as cleared and make sure that just - // as many are sent - int ack_count = kCongestionCount / 2; - EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)).Times(ack_count); - pacer_->UpdateOutstandingData( - DataSize::Bytes(kCongestionWindow - kPacketSize * ack_count)); - - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } - unacked_packets -= ack_count; - ::testing::Mock::VerifyAndClearExpectations(&callback_); - - // Second make sure all packets are sent if sent packets are continuously - // marked as acked. - EXPECT_CALL(callback_, SendPacket(ssrc, _, _, false, _)) - .Times(unacked_packets); - for (int duration = 0; duration < kCongestionTimeMs; duration += 5) { - pacer_->UpdateOutstandingData(DataSize::Zero()); - clock_.AdvanceTimeMilliseconds(5); - pacer_->ProcessPackets(); - } -} - TEST_P(PacingControllerTest, Pause) { uint32_t ssrc_low_priority = 12345; uint32_t ssrc = 12346; @@ -1303,8 +1246,8 @@ TEST_P(PacingControllerTest, Pause) { TEST_P(PacingControllerTest, InactiveFromStart) { // Recreate the pacer without the inital time forwarding. - pacer_ = std::make_unique(&clock_, &callback_, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback_, trials_, + GetParam()); pacer_->SetProbingEnabled(false); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, kTargetRate); @@ -1402,8 +1345,8 @@ TEST_P(PacingControllerTest, ProbingWithInsertedPackets) { uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; - pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->CreateProbeCluster(kSecondClusterRate, @@ -1462,8 +1405,8 @@ TEST_P(PacingControllerTest, SkipsProbesWhenProcessIntervalTooLarge) { "abort_delayed_probes:1,max_probe_delay:2ms/" : "WebRTC-Bwe-ProbingBehavior/" "abort_delayed_probes:0,max_probe_delay:2ms/"); - pacer_ = std::make_unique(&clock_, &packet_sender, - nullptr, &trials, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials, + GetParam()); pacer_->SetPacingRates( DataRate::BitsPerSec(kInitialBitrateBps * kPaceMultiplier), DataRate::BitsPerSec(kInitialBitrateBps)); @@ -1569,8 +1512,8 @@ TEST_P(PacingControllerTest, ProbingWithPaddingSupport) { uint16_t sequence_number = 1234; PacingControllerProbing packet_sender; - pacer_ = std::make_unique(&clock_, &packet_sender, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &packet_sender, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates( @@ -1635,8 +1578,8 @@ TEST_P(PacingControllerTest, PaddingOveruse) { TEST_P(PacingControllerTest, ProbeClusterId) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); Init(); uint32_t ssrc = 12346; @@ -1692,8 +1635,8 @@ TEST_P(PacingControllerTest, ProbeClusterId) { TEST_P(PacingControllerTest, OwnedPacketPrioritizedOnType) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); Init(); // Insert a packet of each type, from low to high priority. Since priority @@ -1739,8 +1682,8 @@ TEST_P(PacingControllerTest, OwnedPacketPrioritizedOnType) { TEST_P(PacingControllerTest, SmallFirstProbePacket) { MockPacketSender callback; - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->CreateProbeCluster(kFirstClusterRate, /*cluster_id=*/0); pacer_->SetPacingRates(kTargetRate * kPaceMultiplier, DataRate::Zero()); @@ -1898,8 +1841,8 @@ TEST_P(PacingControllerTest, uint16_t sequence_number = 1234; MockPacketSender callback; EXPECT_CALL(callback, SendPacket).Times(::testing::AnyNumber()); - pacer_ = std::make_unique(&clock_, &callback, nullptr, - nullptr, GetParam()); + pacer_ = std::make_unique(&clock_, &callback, trials_, + GetParam()); pacer_->SetAccountForAudioPackets(account_for_audio); // First, saturate the padding budget. @@ -2073,9 +2016,13 @@ TEST_P(PacingControllerTest, PaddingTargetAccountsForPaddingRate) { // Re-init pacer with an explicitly set padding target of 10ms; const TimeDelta kPaddingTarget = TimeDelta::Millis(10); - ScopedFieldTrials field_trials( + ExplicitKeyValueConfig field_trials( "WebRTC-Pacer-DynamicPaddingTarget/timedelta:10ms/"); - SetUp(); + srand(0); + // Need to initialize PacingController after we initialize clock. + pacer_ = std::make_unique(&clock_, &callback_, field_trials, + GetParam()); + Init(); const uint32_t kSsrc = 12345; const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125); @@ -2167,6 +2114,54 @@ TEST_P(PacingControllerTest, GapInPacingDoesntAccumulateBudget) { pacer_->ProcessPackets(); } +TEST_P(PacingControllerTest, HandlesSubMicrosecondSendIntervals) { + if (PeriodicProcess()) { + GTEST_SKIP() << "This test checks behavior when not using interval budget."; + } + + static constexpr DataSize kPacketSize = DataSize::Bytes(1); + static constexpr TimeDelta kPacketSendTime = TimeDelta::Micros(1); + + // Set pacing rate such that a packet is sent in 0.5us. + pacer_->SetPacingRates(/*pacing_rate=*/2 * kPacketSize / kPacketSendTime, + /*padding_rate=*/DataRate::Zero()); + + // Enqueue three packets, the first two should be sent immediately - the third + // should cause a non-zero delta to the next process time. + EXPECT_CALL(callback_, SendPacket).Times(2); + for (int i = 0; i < 3; ++i) { + Send(RtpPacketMediaType::kVideo, /*ssrc=*/12345, /*sequence_number=*/i, + clock_.TimeInMilliseconds(), kPacketSize.bytes()); + } + pacer_->ProcessPackets(); + + EXPECT_GT(pacer_->NextSendTime(), clock_.CurrentTime()); +} + +TEST_P(PacingControllerTest, HandlesSubMicrosecondPaddingInterval) { + if (PeriodicProcess()) { + GTEST_SKIP() << "This test checks behavior when not using interval budget."; + } + + static constexpr DataSize kPacketSize = DataSize::Bytes(1); + static constexpr TimeDelta kPacketSendTime = TimeDelta::Micros(1); + + // Set both pacing and padding rates to 1 byte per 0.5us. + pacer_->SetPacingRates(/*pacing_rate=*/2 * kPacketSize / kPacketSendTime, + /*padding_rate=*/2 * kPacketSize / kPacketSendTime); + + // Enqueue and send one packet. + EXPECT_CALL(callback_, SendPacket); + Send(RtpPacketMediaType::kVideo, /*ssrc=*/12345, /*sequence_number=*/1234, + clock_.TimeInMilliseconds(), kPacketSize.bytes()); + pacer_->ProcessPackets(); + + // The padding debt is now 1 byte, and the pacing time for that is lower than + // the precision of a TimeStamp tick. Make sure the pacer still indicates a + // non-zero sleep time is needed until the next process. + EXPECT_GT(pacer_->NextSendTime(), clock_.CurrentTime()); +} + INSTANTIATE_TEST_SUITE_P( WithAndWithoutIntervalBudget, PacingControllerTest, diff --git a/modules/pacing/prioritized_packet_queue.cc b/modules/pacing/prioritized_packet_queue.cc new file mode 100644 index 0000000000..b5c05828d4 --- /dev/null +++ b/modules/pacing/prioritized_packet_queue.cc @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2022 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/pacing/prioritized_packet_queue.h" + +#include + +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +constexpr int kAudioPrioLevel = 0; + +int GetPriorityForType(RtpPacketMediaType type) { + // Lower number takes priority over higher. + switch (type) { + case RtpPacketMediaType::kAudio: + // Audio is always prioritized over other packet types. + return kAudioPrioLevel; + case RtpPacketMediaType::kRetransmission: + // Send retransmissions before new media. + return kAudioPrioLevel + 1; + case RtpPacketMediaType::kVideo: + case RtpPacketMediaType::kForwardErrorCorrection: + // Video has "normal" priority, in the old speak. + // Send redundancy concurrently to video. If it is delayed it might have a + // lower chance of being useful. + return kAudioPrioLevel + 2; + case RtpPacketMediaType::kPadding: + // Packets that are in themselves likely useless, only sent to keep the + // BWE high. + return kAudioPrioLevel + 3; + } + RTC_CHECK_NOTREACHED(); +} + +} // namespace + +DataSize PrioritizedPacketQueue::QueuedPacket::PacketSize() const { + return DataSize::Bytes(packet->payload_size() + packet->padding_size()); +} + +PrioritizedPacketQueue::StreamQueue::StreamQueue(Timestamp creation_time) + : last_enqueue_time_(creation_time) {} + +bool PrioritizedPacketQueue::StreamQueue::EnqueuePacket(QueuedPacket packet, + int priority_level) { + bool first_packet_at_level = packets_[priority_level].empty(); + packets_[priority_level].push_back(std::move(packet)); + return first_packet_at_level; +} + +PrioritizedPacketQueue::QueuedPacket +PrioritizedPacketQueue::StreamQueue::DequePacket(int priority_level) { + RTC_DCHECK(!packets_[priority_level].empty()); + QueuedPacket packet = std::move(packets_[priority_level].front()); + packets_[priority_level].pop_front(); + return packet; +} + +bool PrioritizedPacketQueue::StreamQueue::HasPacketsAtPrio( + int priority_level) const { + return !packets_[priority_level].empty(); +} + +bool PrioritizedPacketQueue::StreamQueue::IsEmpty() const { + for (const std::deque& queue : packets_) { + if (!queue.empty()) { + return false; + } + } + return true; +} + +Timestamp PrioritizedPacketQueue::StreamQueue::LeadingAudioPacketEnqueueTime() + const { + RTC_DCHECK(!packets_[kAudioPrioLevel].empty()); + return packets_[kAudioPrioLevel].begin()->enqueue_time; +} + +Timestamp PrioritizedPacketQueue::StreamQueue::LastEnqueueTime() const { + return last_enqueue_time_; +} + +PrioritizedPacketQueue::PrioritizedPacketQueue(Timestamp creation_time) + : queue_time_sum_(TimeDelta::Zero()), + pause_time_sum_(TimeDelta::Zero()), + size_packets_(0), + size_payload_(DataSize::Zero()), + last_update_time_(creation_time), + paused_(false), + last_culling_time_(creation_time), + top_active_prio_level_(-1) {} + +void PrioritizedPacketQueue::Push(Timestamp enqueue_time, + std::unique_ptr packet) { + StreamQueue* stream_queue; + auto [it, inserted] = streams_.emplace(packet->Ssrc(), nullptr); + if (inserted) { + it->second = std::make_unique(enqueue_time); + } + stream_queue = it->second.get(); + + auto enqueue_time_iterator = + enqueue_times_.insert(enqueue_times_.end(), enqueue_time); + int prio_level = GetPriorityForType(*packet->packet_type()); + RTC_DCHECK_GE(prio_level, 0); + RTC_DCHECK_LT(prio_level, kNumPriorityLevels); + QueuedPacket queued_packed = {.packet = std::move(packet), + .enqueue_time = enqueue_time, + .enqueue_time_iterator = enqueue_time_iterator}; + // In order to figure out how much time a packet has spent in the queue + // while not in a paused state, we subtract the total amount of time the + // queue has been paused so far, and when the packet is popped we subtract + // the total amount of time the queue has been paused at that moment. This + // way we subtract the total amount of time the packet has spent in the + // queue while in a paused state. + UpdateAverageQueueTime(enqueue_time); + queued_packed.enqueue_time -= pause_time_sum_; + ++size_packets_; + size_payload_ += queued_packed.PacketSize(); + + if (stream_queue->EnqueuePacket(std::move(queued_packed), prio_level)) { + // Number packets at `prio_level` for this steam is now non-zero. + streams_by_prio_[prio_level].push_back(stream_queue); + } + if (top_active_prio_level_ < 0 || prio_level < top_active_prio_level_) { + top_active_prio_level_ = prio_level; + } + + static constexpr TimeDelta kTimeout = TimeDelta::Millis(500); + if (enqueue_time - last_culling_time_ > kTimeout) { + for (auto it = streams_.begin(); it != streams_.end();) { + if (it->second->IsEmpty() && + it->second->LastEnqueueTime() + kTimeout < enqueue_time) { + streams_.erase(it++); + } else { + ++it; + } + } + last_culling_time_ = enqueue_time; + } +} + +std::unique_ptr PrioritizedPacketQueue::Pop() { + if (size_packets_ == 0) { + return nullptr; + } + + RTC_DCHECK_GE(top_active_prio_level_, 0); + StreamQueue& stream_queue = *streams_by_prio_[top_active_prio_level_].front(); + QueuedPacket packet = stream_queue.DequePacket(top_active_prio_level_); + --size_packets_; + size_payload_ -= packet.PacketSize(); + + // Calculate the total amount of time spent by this packet in the queue + // while in a non-paused state. Note that the `pause_time_sum_ms_` was + // subtracted from `packet.enqueue_time_ms` when the packet was pushed, and + // by subtracting it now we effectively remove the time spent in in the + // queue while in a paused state. + TimeDelta time_in_non_paused_state = + last_update_time_ - packet.enqueue_time - pause_time_sum_; + queue_time_sum_ -= time_in_non_paused_state; + + RTC_DCHECK(size_packets_ > 0 || queue_time_sum_ == TimeDelta::Zero()); + + RTC_CHECK(packet.enqueue_time_iterator != enqueue_times_.end()); + enqueue_times_.erase(packet.enqueue_time_iterator); + + // Remove StreamQueue from head of fifo-queue for this prio level, and + // and add it to the end if it still has packets. + streams_by_prio_[top_active_prio_level_].pop_front(); + if (stream_queue.HasPacketsAtPrio(top_active_prio_level_)) { + streams_by_prio_[top_active_prio_level_].push_back(&stream_queue); + } else if (streams_by_prio_[top_active_prio_level_].empty()) { + // No stream queues have packets at this prio level, find top priority + // that is not empty. + if (size_packets_ == 0) { + top_active_prio_level_ = -1; + } else { + for (int i = 0; i < kNumPriorityLevels; ++i) { + if (!streams_by_prio_[i].empty()) { + top_active_prio_level_ = i; + break; + } + } + } + } + + return std::move(packet.packet); +} + +int PrioritizedPacketQueue::SizeInPackets() const { + return size_packets_; +} + +DataSize PrioritizedPacketQueue::SizeInPayloadBytes() const { + return size_payload_; +} + +Timestamp PrioritizedPacketQueue::LeadingAudioPacketEnqueueTime() const { + if (streams_by_prio_[kAudioPrioLevel].empty()) { + return Timestamp::MinusInfinity(); + } + return streams_by_prio_[kAudioPrioLevel] + .front() + ->LeadingAudioPacketEnqueueTime(); +} + +Timestamp PrioritizedPacketQueue::OldestEnqueueTime() const { + return enqueue_times_.empty() ? Timestamp::MinusInfinity() + : enqueue_times_.front(); +} + +TimeDelta PrioritizedPacketQueue::AverageQueueTime() const { + if (size_packets_ == 0) { + return TimeDelta::Zero(); + } + return queue_time_sum_ / size_packets_; +} + +void PrioritizedPacketQueue::UpdateAverageQueueTime(Timestamp now) { + RTC_CHECK_GE(now, last_update_time_); + if (now == last_update_time_) { + return; + } + + TimeDelta delta = now - last_update_time_; + + if (paused_) { + pause_time_sum_ += delta; + } else { + queue_time_sum_ += delta * size_packets_; + } + + last_update_time_ = now; +} + +void PrioritizedPacketQueue::SetPauseState(bool paused, Timestamp now) { + UpdateAverageQueueTime(now); + paused_ = paused; +} + +} // namespace webrtc diff --git a/modules/pacing/prioritized_packet_queue.h b/modules/pacing/prioritized_packet_queue.h new file mode 100644 index 0000000000..18b6f4f8f2 --- /dev/null +++ b/modules/pacing/prioritized_packet_queue.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022 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_PACING_PRIORITIZED_PACKET_QUEUE_H_ +#define MODULES_PACING_PRIORITIZED_PACKET_QUEUE_H_ + +#include + +#include +#include +#include +#include + +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/pacing/pacing_controller.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" + +namespace webrtc { + +class PrioritizedPacketQueue : public PacingController::PacketQueue { + public: + explicit PrioritizedPacketQueue(Timestamp creation_time); + PrioritizedPacketQueue(const PrioritizedPacketQueue&) = delete; + PrioritizedPacketQueue& operator=(const PrioritizedPacketQueue&) = delete; + + void Push(Timestamp enqueue_time, + std::unique_ptr packet) override; + std::unique_ptr Pop() override; + int SizeInPackets() const override; + DataSize SizeInPayloadBytes() const override; + Timestamp LeadingAudioPacketEnqueueTime() const override; + Timestamp OldestEnqueueTime() const override; + TimeDelta AverageQueueTime() const override; + void UpdateAverageQueueTime(Timestamp now) override; + void SetPauseState(bool paused, Timestamp now) override; + + private: + static constexpr int kNumPriorityLevels = 4; + + class QueuedPacket { + public: + QueuedPacket(QueuedPacket&&) = default; + QueuedPacket& operator=(QueuedPacket&&) = default; + + QueuedPacket(const QueuedPacket&) = delete; + QueuedPacket& operator=(const QueuedPacket&) = delete; + + DataSize PacketSize() const; + + std::unique_ptr packet; + Timestamp enqueue_time; + std::list::iterator enqueue_time_iterator; + }; + + // Class containing packets for an RTP stream. + // For each priority level, packets are simply stored in a fifo queue. + class StreamQueue { + public: + explicit StreamQueue(Timestamp creation_time); + StreamQueue(StreamQueue&&) = default; + StreamQueue& operator=(StreamQueue&&) = default; + + StreamQueue(const StreamQueue&) = delete; + StreamQueue& operator=(const StreamQueue&) = delete; + + // Enqueue packet at the given priority level. Returns true if the packet + // count for that priority level went from zero to non-zero. + bool EnqueuePacket(QueuedPacket packet, int priority_level); + + QueuedPacket DequePacket(int priority_level); + + bool HasPacketsAtPrio(int priority_level) const; + bool IsEmpty() const; + Timestamp LeadingAudioPacketEnqueueTime() const; + Timestamp LastEnqueueTime() const; + + private: + std::deque packets_[kNumPriorityLevels]; + Timestamp last_enqueue_time_; + }; + + // Cumulative sum, over all packets, of time spent in the queue. + TimeDelta queue_time_sum_; + // Cumulative sum of time the queue has spent in a paused state. + TimeDelta pause_time_sum_; + // Total number of packets stored in this queue. + int size_packets_; + // Sum of payload sizes for all packts stored in this queue. + DataSize size_payload_; + // The last time queue/pause time sums were updated. + Timestamp last_update_time_; + bool paused_; + + // Last time `streams_` was culled for inactive streams. + Timestamp last_culling_time_; + + // Map from SSRC to packet queues for the associated RTP stream. + std::unordered_map> streams_; + + // For each priority level, a queue of StreamQueues which have at least one + // packet pending for that prio level. + std::deque streams_by_prio_[kNumPriorityLevels]; + + // The first index into `stream_by_prio_` that is non-empty. + int top_active_prio_level_; + + // Ordered list of enqueue times. Additions are always increasing and added to + // the end. QueuedPacket instances have a iterators into this list for fast + // removal. + std::list enqueue_times_; +}; + +} // namespace webrtc + +#endif // MODULES_PACING_PRIORITIZED_PACKET_QUEUE_H_ diff --git a/modules/pacing/prioritized_packet_queue_unittest.cc b/modules/pacing/prioritized_packet_queue_unittest.cc new file mode 100644 index 0000000000..d8732e2358 --- /dev/null +++ b/modules/pacing/prioritized_packet_queue_unittest.cc @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2022 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/pacing/prioritized_packet_queue.h" + +#include + +#include "api/units/time_delta.h" +#include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/checks.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +constexpr uint32_t kDefaultSsrc = 123; +constexpr int kDefaultPayloadSize = 789; + +std::unique_ptr CreatePacket(RtpPacketMediaType type, + uint16_t sequence_number, + uint32_t ssrc = kDefaultSsrc) { + auto packet = std::make_unique(/*extensions=*/nullptr); + packet->set_packet_type(type); + packet->SetSsrc(ssrc); + packet->SetSequenceNumber(sequence_number); + packet->SetPayloadSize(kDefaultPayloadSize); + return packet; +} + +} // namespace + +TEST(PrioritizedPacketQueue, ReturnsPacketsInPrioritizedOrder) { + Timestamp now = Timestamp::Zero(); + PrioritizedPacketQueue queue(now); + + // Add packets in low to high packet order. + queue.Push(now, CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); + queue.Push(now, CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); + queue.Push(now, CreatePacket(RtpPacketMediaType::kForwardErrorCorrection, + /*seq=*/3)); + queue.Push(now, CreatePacket(RtpPacketMediaType::kRetransmission, /*seq=*/4)); + queue.Push(now, CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/5)); + + // Packets should be returned in high to low order. + EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); + // Video and FEC prioritized equally - but video was enqueued first. + EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); +} + +TEST(PrioritizedPacketQueue, ReturnsEqualPrioPacketsInRoundRobinOrder) { + Timestamp now = Timestamp::Zero(); + PrioritizedPacketQueue queue(now); + + // Insert video packets (prioritized equally), simulating a simulcast-type use + // case. + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1, /*ssrc=*/100)); + + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2, /*ssrc=*/101)); + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/3, /*ssrc=*/101)); + + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/4, /*ssrc=*/102)); + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/5, /*ssrc=*/102)); + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/6, /*ssrc=*/102)); + queue.Push(now, + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/7, /*ssrc=*/102)); + + // First packet from each SSRC. + EXPECT_EQ(queue.Pop()->SequenceNumber(), 1); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 2); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 4); + + // Second packets from streams that have packets left. + EXPECT_EQ(queue.Pop()->SequenceNumber(), 3); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 5); + + // Only packets from last stream remaining. + EXPECT_EQ(queue.Pop()->SequenceNumber(), 6); + EXPECT_EQ(queue.Pop()->SequenceNumber(), 7); +} + +TEST(PrioritizedPacketQueue, ReportsSizeInPackets) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.SizeInPackets(), 0); + + queue.Push(/*enqueue_time=*/Timestamp::Zero(), + CreatePacket(RtpPacketMediaType::kVideo, + /*seq_no=*/1)); + EXPECT_EQ(queue.SizeInPackets(), 1); + + queue.Pop(); + EXPECT_EQ(queue.SizeInPackets(), 0); +} + +TEST(PrioritizedPacketQueue, ReportsPayloadSize) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); + + queue.Push(/*enqueue_time=*/Timestamp::Zero(), + CreatePacket(RtpPacketMediaType::kVideo, + /*seq_no=*/1)); + EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Bytes(kDefaultPayloadSize)); + + queue.Pop(); + EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); +} + +TEST(PrioritizedPacketQueue, ReportsPaddingSize) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); + static constexpr DataSize kPaddingSize = DataSize::Bytes(190); + + auto packet = std::make_unique(/*extensions=*/nullptr); + packet->set_packet_type(RtpPacketMediaType::kPadding); + packet->SetSsrc(kDefaultSsrc); + packet->SetSequenceNumber(/*seq=*/1); + packet->SetPadding(kPaddingSize.bytes()); + queue.Push(/*enqueue_time=*/Timestamp::Zero(), std::move(packet)); + EXPECT_EQ(queue.SizeInPayloadBytes(), kPaddingSize); + + queue.Pop(); + EXPECT_EQ(queue.SizeInPayloadBytes(), DataSize::Zero()); +} + +TEST(PrioritizedPacketQueue, ReportsOldestEnqueueTime) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::MinusInfinity()); + + // Add three packets, with the middle packet having higher prio. + queue.Push(Timestamp::Millis(10), + CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); + queue.Push(Timestamp::Millis(20), + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); + queue.Push(Timestamp::Millis(30), + CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/3)); + EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(10)); + + queue.Pop(); // Pop packet with enqueue time 20. + EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(10)); + + queue.Pop(); // Pop packet with enqueue time 10. + EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::Millis(30)); + + queue.Pop(); // Pop packet with enqueue time 30, queue empty again. + EXPECT_EQ(queue.OldestEnqueueTime(), Timestamp::MinusInfinity()); +} + +TEST(PrioritizedPacketQueue, ReportsAverageQueueTime) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); + + // Add three packets, with the middle packet having higher prio. + queue.Push(Timestamp::Millis(10), + CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); + queue.Push(Timestamp::Millis(20), + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); + queue.Push(Timestamp::Millis(30), + CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/3)); + + queue.UpdateAverageQueueTime(Timestamp::Millis(40)); + // Packets have waited 30, 20, 10 ms -> average = 20ms. + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(20)); + + queue.Pop(); // Pop packet with enqueue time 20. + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(20)); + + queue.Pop(); // Pop packet with enqueue time 10. + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(10)); + + queue.Pop(); // Pop packet with enqueue time 30, queue empty again. + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); +} + +TEST(PrioritizedPacketQueue, SubtractsPusedTimeFromAverageQueueTime) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Zero()); + + // Add a packet and then enable paused state. + queue.Push(Timestamp::Millis(100), + CreatePacket(RtpPacketMediaType::kPadding, /*seq=*/1)); + queue.SetPauseState(true, Timestamp::Millis(600)); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(500)); + + // Enqueue a packet 500ms into the paused state. Queue time of + // original packet is still seen as 500ms and new one has 0ms giving + // an average of 250ms. + queue.Push(Timestamp::Millis(1100), + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/2)); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(250)); + + // Unpause some time later, queue time still unchanged. + queue.SetPauseState(false, Timestamp::Millis(1600)); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(250)); + + // Update queue time 500ms after pause state ended. + queue.UpdateAverageQueueTime(Timestamp::Millis(2100)); + EXPECT_EQ(queue.AverageQueueTime(), TimeDelta::Millis(750)); +} + +TEST(PrioritizedPacketQueue, ReportsLeadingAudioEnqueueTime) { + PrioritizedPacketQueue queue(/*creation_time=*/Timestamp::Zero()); + EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity()); + + queue.Push(Timestamp::Millis(10), + CreatePacket(RtpPacketMediaType::kVideo, /*seq=*/1)); + EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity()); + + queue.Push(Timestamp::Millis(20), + CreatePacket(RtpPacketMediaType::kAudio, /*seq=*/2)); + + EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::Millis(20)); + + queue.Pop(); // Pop audio packet. + EXPECT_EQ(queue.LeadingAudioPacketEnqueueTime(), Timestamp::MinusInfinity()); +} + +} // namespace webrtc diff --git a/modules/pacing/round_robin_packet_queue.cc b/modules/pacing/round_robin_packet_queue.cc index ef37e5256b..638ab07344 100644 --- a/modules/pacing/round_robin_packet_queue.cc +++ b/modules/pacing/round_robin_packet_queue.cc @@ -20,8 +20,32 @@ namespace webrtc { namespace { static constexpr DataSize kMaxLeadingSize = DataSize::Bytes(1400); + +int GetPriorityForType(RtpPacketMediaType type) { + // Lower number takes priority over higher. + switch (type) { + case RtpPacketMediaType::kAudio: + // Audio is always prioritized over other packet types. + return 0; + case RtpPacketMediaType::kRetransmission: + // Send retransmissions before new media. + return 1; + case RtpPacketMediaType::kVideo: + case RtpPacketMediaType::kForwardErrorCorrection: + // Video has "normal" priority, in the old speak. + // Send redundancy concurrently to video. If it is delayed it might have a + // lower chance of being useful. + return 2; + case RtpPacketMediaType::kPadding: + // Packets that are in themselves likely useless, only sent to keep the + // BWE high. + return 3; + } + RTC_CHECK_NOTREACHED(); } +} // namespace + RoundRobinPacketQueue::QueuedPacket::QueuedPacket(const QueuedPacket& rhs) = default; RoundRobinPacketQueue::QueuedPacket::~QueuedPacket() = default; @@ -29,7 +53,7 @@ RoundRobinPacketQueue::QueuedPacket::~QueuedPacket() = default; RoundRobinPacketQueue::QueuedPacket::QueuedPacket( int priority, Timestamp enqueue_time, - uint64_t enqueue_order, + int64_t enqueue_order, std::multiset::iterator enqueue_time_it, std::unique_ptr packet) : priority_(priority), @@ -70,7 +94,7 @@ bool RoundRobinPacketQueue::QueuedPacket::IsRetransmission() const { return Type() == RtpPacketMediaType::kRetransmission; } -uint64_t RoundRobinPacketQueue::QueuedPacket::EnqueueOrder() const { +int64_t RoundRobinPacketQueue::QueuedPacket::EnqueueOrder() const { return enqueue_order_; } @@ -107,18 +131,10 @@ RoundRobinPacketQueue::Stream::Stream() : size(DataSize::Zero()), ssrc(0) {} RoundRobinPacketQueue::Stream::Stream(const Stream& stream) = default; RoundRobinPacketQueue::Stream::~Stream() = default; -bool IsEnabled(const WebRtcKeyValueConfig* field_trials, const char* name) { - if (!field_trials) { - return false; - } - return absl::StartsWith(field_trials->Lookup(name), "Enabled"); -} - -RoundRobinPacketQueue::RoundRobinPacketQueue( - Timestamp start_time, - const WebRtcKeyValueConfig* field_trials) +RoundRobinPacketQueue::RoundRobinPacketQueue(Timestamp start_time) : transport_overhead_per_packet_(DataSize::Zero()), time_last_updated_(start_time), + enqueue_count_(0), paused_(false), size_packets_(0), size_(DataSize::Zero()), @@ -129,28 +145,27 @@ RoundRobinPacketQueue::RoundRobinPacketQueue( RoundRobinPacketQueue::~RoundRobinPacketQueue() { // Make sure to release any packets owned by raw pointer in QueuedPacket. - while (!Empty()) { + while (size_packets_ > 0) { Pop(); } } -void RoundRobinPacketQueue::Push(int priority, - Timestamp enqueue_time, - uint64_t enqueue_order, +void RoundRobinPacketQueue::Push(Timestamp enqueue_time, std::unique_ptr packet) { RTC_DCHECK(packet->packet_type().has_value()); + int priority = GetPriorityForType(*packet->packet_type()); if (size_packets_ == 0) { // Single packet fast-path. single_packet_queue_.emplace( - QueuedPacket(priority, enqueue_time, enqueue_order, + QueuedPacket(priority, enqueue_time, enqueue_count_++, enqueue_times_.end(), std::move(packet))); - UpdateQueueTime(enqueue_time); + UpdateAverageQueueTime(enqueue_time); single_packet_queue_->SubtractPauseTime(pause_time_sum_); size_packets_ = 1; size_ += PacketSize(*single_packet_queue_); } else { MaybePromoteSinglePacketToNormalQueue(); - Push(QueuedPacket(priority, enqueue_time, enqueue_order, + Push(QueuedPacket(priority, enqueue_time, enqueue_count_++, enqueue_times_.insert(enqueue_time), std::move(packet))); } } @@ -167,7 +182,7 @@ std::unique_ptr RoundRobinPacketQueue::Pop() { return rtp_packet; } - RTC_DCHECK(!Empty()); + RTC_DCHECK_GT(size_packets_, 0); Stream* stream = GetHighestPriorityStream(); const QueuedPacket& queued_packet = stream->packet_queue.top(); @@ -216,34 +231,24 @@ std::unique_ptr RoundRobinPacketQueue::Pop() { return rtp_packet; } -bool RoundRobinPacketQueue::Empty() const { - if (size_packets_ == 0) { - RTC_DCHECK(!single_packet_queue_.has_value() && stream_priorities_.empty()); - return true; - } - RTC_DCHECK(single_packet_queue_.has_value() || !stream_priorities_.empty()); - return false; -} - -size_t RoundRobinPacketQueue::SizeInPackets() const { +int RoundRobinPacketQueue::SizeInPackets() const { return size_packets_; } -DataSize RoundRobinPacketQueue::Size() const { +DataSize RoundRobinPacketQueue::SizeInPayloadBytes() const { return size_; } -absl::optional RoundRobinPacketQueue::LeadingAudioPacketEnqueueTime() - const { +Timestamp RoundRobinPacketQueue::LeadingAudioPacketEnqueueTime() const { if (single_packet_queue_.has_value()) { if (single_packet_queue_->Type() == RtpPacketMediaType::kAudio) { return single_packet_queue_->EnqueueTime(); } - return absl::nullopt; + return Timestamp::MinusInfinity(); } if (stream_priorities_.empty()) { - return absl::nullopt; + return Timestamp::MinusInfinity(); } uint32_t ssrc = stream_priorities_.begin()->second; @@ -251,7 +256,7 @@ absl::optional RoundRobinPacketQueue::LeadingAudioPacketEnqueueTime() if (top_packet.Type() == RtpPacketMediaType::kAudio) { return top_packet.EnqueueTime(); } - return absl::nullopt; + return Timestamp::MinusInfinity(); } Timestamp RoundRobinPacketQueue::OldestEnqueueTime() const { @@ -259,13 +264,13 @@ Timestamp RoundRobinPacketQueue::OldestEnqueueTime() const { return single_packet_queue_->EnqueueTime(); } - if (Empty()) + if (size_packets_ == 0) return Timestamp::MinusInfinity(); RTC_CHECK(!enqueue_times_.empty()); return *enqueue_times_.begin(); } -void RoundRobinPacketQueue::UpdateQueueTime(Timestamp now) { +void RoundRobinPacketQueue::UpdateAverageQueueTime(Timestamp now) { RTC_CHECK_GE(now, time_last_updated_); if (now == time_last_updated_) return; @@ -275,7 +280,7 @@ void RoundRobinPacketQueue::UpdateQueueTime(Timestamp now) { if (paused_) { pause_time_sum_ += delta; } else { - queue_time_sum_ += TimeDelta::Micros(delta.us() * size_packets_); + queue_time_sum_ += delta * size_packets_; } time_last_updated_ = now; @@ -284,38 +289,12 @@ void RoundRobinPacketQueue::UpdateQueueTime(Timestamp now) { void RoundRobinPacketQueue::SetPauseState(bool paused, Timestamp now) { if (paused_ == paused) return; - UpdateQueueTime(now); + UpdateAverageQueueTime(now); paused_ = paused; } -void RoundRobinPacketQueue::SetIncludeOverhead() { - MaybePromoteSinglePacketToNormalQueue(); - include_overhead_ = true; - // We need to update the size to reflect overhead for existing packets. - for (const auto& stream : streams_) { - for (const QueuedPacket& packet : stream.second.packet_queue) { - size_ += DataSize::Bytes(packet.RtpPacket()->headers_size()) + - transport_overhead_per_packet_; - } - } -} - -void RoundRobinPacketQueue::SetTransportOverhead(DataSize overhead_per_packet) { - MaybePromoteSinglePacketToNormalQueue(); - if (include_overhead_) { - DataSize previous_overhead = transport_overhead_per_packet_; - // We need to update the size to reflect overhead for existing packets. - for (const auto& stream : streams_) { - int packets = stream.second.packet_queue.size(); - size_ -= packets * previous_overhead; - size_ += packets * overhead_per_packet; - } - } - transport_overhead_per_packet_ = overhead_per_packet; -} - TimeDelta RoundRobinPacketQueue::AverageQueueTime() const { - if (Empty()) + if (size_packets_ == 0) return TimeDelta::Zero(); return queue_time_sum_ / size_packets_; } @@ -356,7 +335,7 @@ void RoundRobinPacketQueue::Push(QueuedPacket packet) { // the total amount of time the queue has been paused at that moment. This // way we subtract the total amount of time the packet has spent in the // queue while in a paused state. - UpdateQueueTime(packet.EnqueueTime()); + UpdateAverageQueueTime(packet.EnqueueTime()); packet.SubtractPauseTime(pause_time_sum_); size_packets_ += 1; diff --git a/modules/pacing/round_robin_packet_queue.h b/modules/pacing/round_robin_packet_queue.h index dd35b90d93..4d6f93ba2d 100644 --- a/modules/pacing/round_robin_packet_queue.h +++ b/modules/pacing/round_robin_packet_queue.h @@ -22,50 +22,38 @@ #include #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" +#include "modules/pacing/pacing_controller.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 { -class RoundRobinPacketQueue { +class RoundRobinPacketQueue : public PacingController::PacketQueue { public: - RoundRobinPacketQueue(Timestamp start_time, - const WebRtcKeyValueConfig* field_trials); + explicit RoundRobinPacketQueue(Timestamp start_time); ~RoundRobinPacketQueue(); - void Push(int priority, - Timestamp enqueue_time, - uint64_t enqueue_order, - std::unique_ptr packet); - std::unique_ptr Pop(); - - bool Empty() const; - size_t SizeInPackets() const; - DataSize Size() const; - // If the next packet, that would be returned by Pop() if called - // now, is an audio packet this method returns the enqueue time - // of that packet. If queue is empty or top packet is not audio, - // returns nullopt. - absl::optional LeadingAudioPacketEnqueueTime() const; - - Timestamp OldestEnqueueTime() const; - TimeDelta AverageQueueTime() const; - void UpdateQueueTime(Timestamp now); - void SetPauseState(bool paused, Timestamp now); - void SetIncludeOverhead(); - void SetTransportOverhead(DataSize overhead_per_packet); + void Push(Timestamp enqueue_time, + std::unique_ptr packet) override; + std::unique_ptr Pop() override; + + int SizeInPackets() const override; + DataSize SizeInPayloadBytes() const override; + Timestamp LeadingAudioPacketEnqueueTime() const override; + Timestamp OldestEnqueueTime() const override; + TimeDelta AverageQueueTime() const override; + void UpdateAverageQueueTime(Timestamp now) override; + void SetPauseState(bool paused, Timestamp now) override; private: struct QueuedPacket { public: QueuedPacket(int priority, Timestamp enqueue_time, - uint64_t enqueue_order, + int64_t enqueue_order, std::multiset::iterator enqueue_time_it, std::unique_ptr packet); QueuedPacket(const QueuedPacket& rhs); @@ -78,7 +66,7 @@ class RoundRobinPacketQueue { uint32_t Ssrc() const; Timestamp EnqueueTime() const; bool IsRetransmission() const; - uint64_t EnqueueOrder() const; + int64_t EnqueueOrder() const; RtpPacketToSend* RtpPacket() const; std::multiset::iterator EnqueueTimeIterator() const; @@ -88,7 +76,7 @@ class RoundRobinPacketQueue { private: int priority_; Timestamp enqueue_time_; // Absolute time of pacer queue entry. - uint64_t enqueue_order_; + int64_t enqueue_order_; bool is_retransmission_; // Cached for performance. std::multiset::iterator enqueue_time_it_; // Raw pointer since priority_queue doesn't allow for moving @@ -150,8 +138,10 @@ class RoundRobinPacketQueue { Timestamp time_last_updated_; + int64_t enqueue_count_; + bool paused_; - size_t size_packets_; + int size_packets_; DataSize size_; DataSize max_size_; TimeDelta queue_time_sum_; diff --git a/modules/pacing/rtp_packet_pacer.h b/modules/pacing/rtp_packet_pacer.h index 3dc2b27612..a201838858 100644 --- a/modules/pacing/rtp_packet_pacer.h +++ b/modules/pacing/rtp_packet_pacer.h @@ -34,8 +34,7 @@ class RtpPacketPacer { // Resume sending packets. virtual void Resume() = 0; - virtual void SetCongestionWindow(DataSize congestion_window_size) = 0; - virtual void UpdateOutstandingData(DataSize outstanding_data) = 0; + virtual void SetCongested(bool congested) = 0; // Sets the pacing rates. Must be called once before packets can be sent. virtual void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) = 0; diff --git a/modules/pacing/task_queue_paced_sender.cc b/modules/pacing/task_queue_paced_sender.cc index c58241fdd8..a597f07377 100644 --- a/modules/pacing/task_queue_paced_sender.cc +++ b/modules/pacing/task_queue_paced_sender.cc @@ -12,39 +12,61 @@ #include #include + #include "absl/memory/memory.h" #include "rtc_base/checks.h" -#include "rtc_base/event.h" -#include "rtc_base/logging.h" -#include "rtc_base/task_utils/to_queued_task.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/experiments/field_trial_units.h" #include "rtc_base/trace_event.h" namespace webrtc { +namespace { + +constexpr const char* kSlackedTaskQueuePacedSenderFieldTrial = + "WebRTC-SlackedTaskQueuePacedSender"; + +} // namespace + +const int TaskQueuePacedSender::kNoPacketHoldback = -1; + +TaskQueuePacedSender::SlackedPacerFlags::SlackedPacerFlags( + const FieldTrialsView& field_trials) + : allow_low_precision("Enabled"), + max_low_precision_expected_queue_time("max_queue_time") { + ParseFieldTrial( + {&allow_low_precision, &max_low_precision_expected_queue_time}, + field_trials.Lookup(kSlackedTaskQueuePacedSenderFieldTrial)); +} + TaskQueuePacedSender::TaskQueuePacedSender( Clock* clock, PacingController::PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, + const FieldTrialsView& field_trials, TaskQueueFactory* task_queue_factory, TimeDelta max_hold_back_window, int max_hold_back_window_in_packets) : clock_(clock), - max_hold_back_window_(max_hold_back_window), - max_hold_back_window_in_packets_(max_hold_back_window_in_packets), + slacked_pacer_flags_(field_trials), + max_hold_back_window_(slacked_pacer_flags_.allow_low_precision + ? PacingController::kMinSleepTime + : max_hold_back_window), + max_hold_back_window_in_packets_(slacked_pacer_flags_.allow_low_precision + ? 0 + : max_hold_back_window_in_packets), pacing_controller_(clock, packet_sender, - event_log, field_trials, PacingController::ProcessMode::kDynamic), next_process_time_(Timestamp::MinusInfinity()), is_started_(false), is_shutdown_(false), packet_size_(/*alpha=*/0.95), + include_overhead_(false), task_queue_(task_queue_factory->CreateTaskQueue( "TaskQueuePacedSender", TaskQueueFactory::Priority::NORMAL)) { - packet_size_.Apply(1, 0); + RTC_DCHECK_GE(max_hold_back_window_, PacingController::kMinSleepTime); } TaskQueuePacedSender::~TaskQueuePacedSender() { @@ -89,28 +111,10 @@ void TaskQueuePacedSender::Resume() { }); } -void TaskQueuePacedSender::SetCongestionWindow( - DataSize congestion_window_size) { - task_queue_.PostTask([this, congestion_window_size]() { - RTC_DCHECK_RUN_ON(&task_queue_); - pacing_controller_.SetCongestionWindow(congestion_window_size); - MaybeProcessPackets(Timestamp::MinusInfinity()); - }); -} - -void TaskQueuePacedSender::UpdateOutstandingData(DataSize outstanding_data) { - if (task_queue_.IsCurrent()) { - RTC_DCHECK_RUN_ON(&task_queue_); - // Fast path since this can be called once per sent packet while on the - // task queue. - pacing_controller_.UpdateOutstandingData(outstanding_data); - MaybeProcessPackets(Timestamp::MinusInfinity()); - return; - } - - task_queue_.PostTask([this, outstanding_data]() { +void TaskQueuePacedSender::SetCongested(bool congested) { + task_queue_.PostTask([this, congested]() { RTC_DCHECK_RUN_ON(&task_queue_); - pacing_controller_.UpdateOutstandingData(outstanding_data); + pacing_controller_.SetCongested(congested); MaybeProcessPackets(Timestamp::MinusInfinity()); }); } @@ -140,8 +144,12 @@ void TaskQueuePacedSender::EnqueuePackets( task_queue_.PostTask([this, packets_ = std::move(packets)]() mutable { RTC_DCHECK_RUN_ON(&task_queue_); for (auto& packet : packets_) { - packet_size_.Apply(1, packet->size()); - RTC_DCHECK_GE(packet->capture_time_ms(), 0); + size_t packet_size = packet->payload_size() + packet->padding_size(); + if (include_overhead_) { + packet_size += packet->headers_size(); + } + packet_size_.Apply(1, packet_size); + RTC_DCHECK_GE(packet->capture_time(), Timestamp::Zero()); pacing_controller_.EnqueuePacket(std::move(packet)); } MaybeProcessPackets(Timestamp::MinusInfinity()); @@ -159,6 +167,7 @@ void TaskQueuePacedSender::SetAccountForAudioPackets(bool account_for_audio) { void TaskQueuePacedSender::SetIncludeOverhead() { task_queue_.PostTask([this]() { RTC_DCHECK_RUN_ON(&task_queue_); + include_overhead_ = true; pacing_controller_.SetIncludeOverhead(); MaybeProcessPackets(Timestamp::MinusInfinity()); }); @@ -194,13 +203,16 @@ absl::optional TaskQueuePacedSender::FirstSentPacketTime() const { TimeDelta TaskQueuePacedSender::OldestPacketWaitTime() const { Timestamp oldest_packet = GetStats().oldest_packet_enqueue_time; - if (oldest_packet.IsInfinite()) + if (oldest_packet.IsInfinite()) { return TimeDelta::Zero(); + } // (webrtc:9716): The clock is not always monotonic. Timestamp current = clock_->CurrentTime(); - if (current < oldest_packet) + if (current < oldest_packet) { return TimeDelta::Zero(); + } + return current - oldest_packet; } @@ -213,70 +225,91 @@ void TaskQueuePacedSender::MaybeProcessPackets( Timestamp scheduled_process_time) { RTC_DCHECK_RUN_ON(&task_queue_); +#if RTC_TRACE_EVENTS_ENABLED + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webrtc"), + "TaskQueuePacedSender::MaybeProcessPackets"); +#endif + if (is_shutdown_ || !is_started_) { return; } - // Normally, run ProcessPackets() only if this is the scheduled task. - // If it is not but it is already time to process and there either is - // no scheduled task or the schedule has shifted forward in time, run - // anyway and clear any schedule. - Timestamp next_process_time = pacing_controller_.NextSendTime(); + Timestamp next_send_time = pacing_controller_.NextSendTime(); + RTC_DCHECK(next_send_time.IsFinite()); const Timestamp now = clock_->CurrentTime(); - const bool is_scheduled_call = next_process_time_ == scheduled_process_time; - if (is_scheduled_call) { - // Indicate no pending scheduled call. - next_process_time_ = Timestamp::MinusInfinity(); - } - if (is_scheduled_call || - (now >= next_process_time && (next_process_time_.IsInfinite() || - next_process_time < next_process_time_))) { + TimeDelta early_execute_margin = + pacing_controller_.IsProbing() + ? PacingController::kMaxEarlyProbeProcessing + : TimeDelta::Zero(); + + // Process packets and update stats. + while (next_send_time <= now + early_execute_margin) { pacing_controller_.ProcessPackets(); - next_process_time = pacing_controller_.NextSendTime(); + next_send_time = pacing_controller_.NextSendTime(); + RTC_DCHECK(next_send_time.IsFinite()); + + // Probing state could change. Get margin after process packets. + early_execute_margin = pacing_controller_.IsProbing() + ? PacingController::kMaxEarlyProbeProcessing + : TimeDelta::Zero(); } + UpdateStats(); - TimeDelta hold_back_window = max_hold_back_window_; - DataRate pacing_rate = pacing_controller_.pacing_rate(); - DataSize avg_packet_size = DataSize::Bytes(packet_size_.filtered()); - if (max_hold_back_window_in_packets_ > 0 && !pacing_rate.IsZero() && - !avg_packet_size.IsZero()) { - TimeDelta avg_packet_send_time = avg_packet_size / pacing_rate; - hold_back_window = - std::min(hold_back_window, - avg_packet_send_time * max_hold_back_window_in_packets_); + // Ignore retired scheduled task, otherwise reset `next_process_time_`. + if (scheduled_process_time.IsFinite()) { + if (scheduled_process_time != next_process_time_) { + return; + } + next_process_time_ = Timestamp::MinusInfinity(); } - absl::optional time_to_next_process; - if (pacing_controller_.IsProbing() && - next_process_time != next_process_time_) { - // If we're probing and there isn't already a wakeup scheduled for the next - // process time, always post a task and just round sleep time down to - // nearest millisecond. - if (next_process_time.IsMinusInfinity()) { - time_to_next_process = TimeDelta::Zero(); - } else { - time_to_next_process = - std::max(TimeDelta::Zero(), - (next_process_time - now).RoundDownTo(TimeDelta::Millis(1))); + // Do not hold back in probing. + TimeDelta hold_back_window = TimeDelta::Zero(); + if (!pacing_controller_.IsProbing()) { + hold_back_window = max_hold_back_window_; + DataRate pacing_rate = pacing_controller_.pacing_rate(); + if (max_hold_back_window_in_packets_ != kNoPacketHoldback && + !pacing_rate.IsZero() && + packet_size_.filtered() != rtc::ExpFilter::kValueUndefined) { + TimeDelta avg_packet_send_time = + DataSize::Bytes(packet_size_.filtered()) / pacing_rate; + hold_back_window = + std::min(hold_back_window, + avg_packet_send_time * max_hold_back_window_in_packets_); } - } else if (next_process_time_.IsMinusInfinity() || - next_process_time <= next_process_time_ - hold_back_window) { - // Schedule a new task since there is none currently scheduled - // (`next_process_time_` is infinite), or the new process time is at least - // one holdback window earlier than whatever is currently scheduled. - time_to_next_process = std::max(next_process_time - now, hold_back_window); } - if (time_to_next_process) { - // Set a new scheduled process time and post a delayed task. - next_process_time_ = next_process_time; + // Calculate next process time. + TimeDelta time_to_next_process = + std::max(hold_back_window, next_send_time - now - early_execute_margin); + next_send_time = now + time_to_next_process; + + // If no in flight task or in flight task is later than `next_send_time`, + // schedule a new one. Previous in flight task will be retired. + if (next_process_time_.IsMinusInfinity() || + next_process_time_ > next_send_time) { + // Prefer low precision if allowed and not probing. + TaskQueueBase::DelayPrecision precision = + slacked_pacer_flags_.allow_low_precision && + !pacing_controller_.IsProbing() + ? TaskQueueBase::DelayPrecision::kLow + : TaskQueueBase::DelayPrecision::kHigh; + // Optionally disable low precision if the expected queue time is greater + // than `max_low_precision_expected_queue_time`. + if (precision == TaskQueueBase::DelayPrecision::kLow && + slacked_pacer_flags_.max_low_precision_expected_queue_time && + pacing_controller_.ExpectedQueueTime() >= + slacked_pacer_flags_.max_low_precision_expected_queue_time + .Value()) { + precision = TaskQueueBase::DelayPrecision::kHigh; + } - task_queue_.PostDelayedHighPrecisionTask( - [this, next_process_time]() { MaybeProcessPackets(next_process_time); }, - time_to_next_process->ms()); + task_queue_.PostDelayedTaskWithPrecision( + precision, + [this, next_send_time]() { MaybeProcessPackets(next_send_time); }, + time_to_next_process.RoundUpTo(TimeDelta::Millis(1)).ms()); + next_process_time_ = next_send_time; } - - UpdateStats(); } void TaskQueuePacedSender::UpdateStats() { diff --git a/modules/pacing/task_queue_paced_sender.h b/modules/pacing/task_queue_paced_sender.h index 353f137963..32c5c1f847 100644 --- a/modules/pacing/task_queue_paced_sender.h +++ b/modules/pacing/task_queue_paced_sender.h @@ -14,12 +14,12 @@ #include #include -#include #include -#include #include +#include "absl/base/attributes.h" #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "api/task_queue/task_queue_factory.h" #include "api/units/data_size.h" @@ -28,30 +28,28 @@ #include "modules/pacing/pacing_controller.h" #include "modules/pacing/rtp_packet_pacer.h" #include "modules/rtp_rtcp/source/rtp_packet_to_send.h" +#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/numerics/exp_filter.h" -#include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_queue.h" #include "rtc_base/thread_annotations.h" namespace webrtc { class Clock; -class RtcEventLog; class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { public: + static const int kNoPacketHoldback; + // The `hold_back_window` parameter sets a lower bound on time to sleep if // there is currently a pacer queue and packets can't immediately be // processed. Increasing this reduces thread wakeups at the expense of higher // latency. - // TODO(bugs.webrtc.org/10809): Remove default values. - TaskQueuePacedSender( - Clock* clock, - PacingController::PacketSender* packet_sender, - RtcEventLog* event_log, - const WebRtcKeyValueConfig* field_trials, - TaskQueueFactory* task_queue_factory, - TimeDelta max_hold_back_window = PacingController::kMinSleepTime, - int max_hold_back_window_in_packets = -1); + TaskQueuePacedSender(Clock* clock, + PacingController::PacketSender* packet_sender, + const FieldTrialsView& field_trials, + TaskQueueFactory* task_queue_factory, + TimeDelta max_hold_back_window, + int max_hold_back_window_in_packets); ~TaskQueuePacedSender() override; @@ -75,8 +73,7 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // Resume sending packets. void Resume() override; - void SetCongestionWindow(DataSize congestion_window_size) override; - void UpdateOutstandingData(DataSize outstanding_data) override; + void SetCongested(bool congested) override; // Sets the pacing rates. Must be called once before packets can be sent. void SetPacingRates(DataRate pacing_rate, DataRate padding_rate) override; @@ -133,6 +130,24 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { Stats GetStats() const; Clock* const clock_; + struct SlackedPacerFlags { + // Parses `kSlackedTaskQueuePacedSenderFieldTrial`. Example: + // --force-fieldtrials=WebRTC-SlackedTaskQueuePacedSender/Enabled,max_queue_time:75ms/ + explicit SlackedPacerFlags(const FieldTrialsView& field_trials); + // When "Enabled", delayed tasks invoking MaybeProcessPackets() are + // scheduled using low precision instead of high precision, resulting in + // less idle wake ups and packets being sent in bursts if the `task_queue_` + // implementation supports slack. When probing, high precision is used + // regardless to ensure good bandwidth estimation. + FieldTrialFlag allow_low_precision; + // Controlled via the "max_queue_time" experiment arm. If set, uses high + // precision scheduling of MaybeProcessPackets() whenever the expected queue + // time is greater than or equal to this value. + FieldTrialOptional max_low_precision_expected_queue_time; + }; + const SlackedPacerFlags slacked_pacer_flags_; + // The holdback window prevents too frequent delayed MaybeProcessPackets() + // calls. These are only applicable if `allow_low_precision` is false. const TimeDelta max_hold_back_window_; const int max_hold_back_window_in_packets_; @@ -156,6 +171,7 @@ class TaskQueuePacedSender : public RtpPacketPacer, public RtpPacketSender { // Filtered size of enqueued packets, in bytes. rtc::ExpFilter packet_size_ RTC_GUARDED_BY(task_queue_); + bool include_overhead_ RTC_GUARDED_BY(task_queue_); mutable Mutex stats_mutex_; Stats current_stats_ RTC_GUARDED_BY(stats_mutex_); diff --git a/modules/pacing/task_queue_paced_sender_unittest.cc b/modules/pacing/task_queue_paced_sender_unittest.cc index d78365d499..7b05110822 100644 --- a/modules/pacing/task_queue_paced_sender_unittest.cc +++ b/modules/pacing/task_queue_paced_sender_unittest.cc @@ -11,18 +11,22 @@ #include "modules/pacing/task_queue_paced_sender.h" #include +#include #include #include #include #include #include +#include "api/task_queue/task_queue_base.h" #include "api/transport/network_types.h" +#include "api/units/data_rate.h" #include "modules/pacing/packet_router.h" #include "modules/utility/include/mock/mock_process_thread.h" -#include "test/field_trial.h" +#include "rtc_base/task_utils/to_queued_task.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" using ::testing::_; @@ -37,7 +41,6 @@ constexpr uint32_t kVideoSsrc = 234565; constexpr uint32_t kVideoRtxSsrc = 34567; constexpr uint32_t kFlexFecSsrc = 45678; constexpr size_t kDefaultPacketSize = 1234; -constexpr int kNoPacketHoldback = -1; class MockPacketRouter : public PacketRouter { public: @@ -76,6 +79,88 @@ std::vector> GeneratePadding( return padding_packets; } +class TaskQueueWithFakePrecisionFactory : public TaskQueueFactory { + public: + explicit TaskQueueWithFakePrecisionFactory( + TaskQueueFactory* task_queue_factory) + : task_queue_factory_(task_queue_factory) {} + + std::unique_ptr CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + return std::unique_ptr( + new TaskQueueWithFakePrecision( + const_cast(this), + task_queue_factory_)); + } + + int delayed_low_precision_count() const { + return delayed_low_precision_count_; + } + int delayed_high_precision_count() const { + return delayed_high_precision_count_; + } + + private: + friend class TaskQueueWithFakePrecision; + + class TaskQueueWithFakePrecision : public TaskQueueBase { + public: + TaskQueueWithFakePrecision( + TaskQueueWithFakePrecisionFactory* parent_factory, + TaskQueueFactory* task_queue_factory) + : parent_factory_(parent_factory), + task_queue_(task_queue_factory->CreateTaskQueue( + "TaskQueueWithFakePrecision", + TaskQueueFactory::Priority::NORMAL)) {} + ~TaskQueueWithFakePrecision() override {} + + void Delete() override { + // `task_queue_->Delete()` is implicitly called in the destructor due to + // TaskQueueDeleter. + delete this; + } + void PostTask(std::unique_ptr task) override { + task_queue_->PostTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + })); + } + void PostDelayedTask(std::unique_ptr task, + uint32_t milliseconds) override { + ++parent_factory_->delayed_low_precision_count_; + task_queue_->PostDelayedTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + }), + milliseconds); + } + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) override { + ++parent_factory_->delayed_high_precision_count_; + task_queue_->PostDelayedHighPrecisionTask( + ToQueuedTask([this, task = std::move(task)]() mutable { + RunTask(std::move(task)); + }), + milliseconds); + } + + private: + void RunTask(std::unique_ptr task) { + CurrentTaskQueueSetter set_current(this); + if (!task->Run()) + task.release(); + } + + TaskQueueWithFakePrecisionFactory* parent_factory_; + std::unique_ptr task_queue_; + }; + + TaskQueueFactory* task_queue_factory_; + std::atomic delayed_low_precision_count_ = 0u; + std::atomic delayed_high_precision_count_ = 0u; +}; + } // namespace namespace test { @@ -116,11 +201,11 @@ std::vector> GeneratePackets( TEST(TaskQueuePacedSenderTest, PacesPackets) { GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); // Insert a number of packets, covering one second. static constexpr size_t kPacketsToSend = 42; @@ -156,11 +241,11 @@ TEST(TaskQueuePacedSenderTest, PacesPackets) { TEST(TaskQueuePacedSenderTest, ReschedulesProcessOnRateChange) { GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); // Insert a number of packets to be sent 200ms apart. const size_t kPacketsPerSecond = 5; @@ -208,11 +293,11 @@ TEST(TaskQueuePacedSenderTest, ReschedulesProcessOnRateChange) { TEST(TaskQueuePacedSenderTest, SendsAudioImmediately) { GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); const DataRate kPacingDataRate = DataRate::KilobitsPerSec(125); const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); @@ -241,11 +326,11 @@ TEST(TaskQueuePacedSenderTest, SleepsDuringCoalscingWindow) { const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, time_controller.GetTaskQueueFactory(), - kCoalescingWindow, kNoPacketHoldback); + kCoalescingWindow, + TaskQueuePacedSender::kNoPacketHoldback); // Set rates so one packet adds one ms of buffer level. const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); @@ -278,11 +363,11 @@ TEST(TaskQueuePacedSenderTest, ProbingOverridesCoalescingWindow) { const TimeDelta kCoalescingWindow = TimeDelta::Millis(5); GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, time_controller.GetTaskQueueFactory(), - kCoalescingWindow, kNoPacketHoldback); + kCoalescingWindow, + TaskQueuePacedSender::kNoPacketHoldback); // Set rates so one packet adds one ms of buffer level. const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); @@ -306,15 +391,15 @@ TEST(TaskQueuePacedSenderTest, ProbingOverridesCoalescingWindow) { time_controller.AdvanceTime(kCoalescingWindow - TimeDelta::Millis(1)); } -TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSetTime) { - ScopedFieldTrials trials("WebRTC-Bwe-ProbingBehavior/min_probe_delta:1ms/"); +TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSentTime) { + ScopedKeyValueConfig trials( + "WebRTC-Bwe-ProbingBehavior/min_probe_delta:1ms/"); GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); // Set rates so one packet adds 4ms of buffer level. const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); @@ -339,15 +424,16 @@ TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSetTime) { // Advance to less than 3ms before next packet send time. time_controller.AdvanceTime(TimeDelta::Micros(1001)); - // Trigger a probe at 4x the current pacing rate and insert the number of + // Trigger a probe at 2x the current pacing rate and insert the number of // packets the probe needs. const DataRate kProbeRate = 2 * kPacingDataRate; const int kProbeClusterId = 1; pacer.CreateProbeCluster(kProbeRate, kProbeClusterId); - // Expected size for each probe in a cluster is twice the expected bits - // sent during min_probe_delta. - // Expect one additional call since probe always starts with a small + // Expected size for each probe in a cluster is twice the expected bits sent + // during min_probe_delta. + // Expect one additional call since probe always starts with a small (1 byte) + // padding packet that's not counted into the probe rate here. const TimeDelta kProbeTimeDelta = TimeDelta::Millis(2); const DataSize kProbeSize = kProbeRate * kProbeTimeDelta; const size_t kNumPacketsInProbe = @@ -374,14 +460,14 @@ TEST(TaskQueuePacedSenderTest, SchedulesProbeAtSetTime) { TEST(TaskQueuePacedSenderTest, NoMinSleepTimeWhenProbing) { // Set min_probe_delta to be less than kMinSleepTime (1ms). const TimeDelta kMinProbeDelta = TimeDelta::Micros(100); - ScopedFieldTrials trials("WebRTC-Bwe-ProbingBehavior/min_probe_delta:100us/"); + ScopedKeyValueConfig trials( + "WebRTC-Bwe-ProbingBehavior/min_probe_delta:100us/"); GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); // Set rates so one packet adds 4ms of buffer level. const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); @@ -423,8 +509,8 @@ TEST(TaskQueuePacedSenderTest, NoMinSleepTimeWhenProbing) { // Verify the amount of probing data sent. // Probe always starts with a small (1 byte) padding packet that's not // counted into the probe rate here. - EXPECT_EQ(data_sent, - kProbingRate * TimeDelta::Millis(1) + DataSize::Bytes(1)); + const DataSize kMinProbeSize = 2 * kMinProbeDelta * kProbingRate; + EXPECT_EQ(data_sent, DataSize::Bytes(1) + kPacketSize + 4 * kMinProbeSize); } TEST(TaskQueuePacedSenderTest, PacketBasedCoalescing) { @@ -433,9 +519,8 @@ TEST(TaskQueuePacedSenderTest, PacketBasedCoalescing) { GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, time_controller.GetTaskQueueFactory(), kFixedCoalescingWindow, kPacketBasedHoldback); @@ -484,9 +569,8 @@ TEST(TaskQueuePacedSenderTest, FixedHoldBackHasPriorityOverPackets) { GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); MockPacketRouter packet_router; - TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, time_controller.GetTaskQueueFactory(), kFixedCoalescingWindow, kPacketBasedHoldback); @@ -526,15 +610,59 @@ TEST(TaskQueuePacedSenderTest, FixedHoldBackHasPriorityOverPackets) { time_controller.AdvanceTime(kFixedCoalescingWindow); } +TEST(TaskQueuePacedSenderTest, ProbingStopDuringSendLoop) { + // Set a low `min_probe_delta` to let probing finish during send loop. + ScopedKeyValueConfig trials( + "WebRTC-Bwe-ProbingBehavior/min_probe_delta:100us/"); + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + MockPacketRouter packet_router; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); + + // Set rates so 2 packets adds 1ms of buffer level. + const DataSize kPacketSize = DataSize::Bytes(kDefaultPacketSize); + const TimeDelta kPacketPacingTime = TimeDelta::Millis(1); + const DataRate kPacingDataRate = 2 * kPacketSize / kPacketPacingTime; + + pacer.SetPacingRates(kPacingDataRate, DataRate::Zero()); + pacer.EnsureStarted(); + + EXPECT_CALL(packet_router, FetchFec).WillRepeatedly([]() { + return std::vector>(); + }); + EXPECT_CALL(packet_router, GeneratePadding(_)) + .WillRepeatedly( + [](DataSize target_size) { return GeneratePadding(target_size); }); + + // Set probe rate. + const int kProbeClusterId = 1; + const DataRate kProbingRate = kPacingDataRate; + pacer.CreateProbeCluster(kProbingRate, kProbeClusterId); + + const int kPacketsToSend = 100; + const TimeDelta kPacketsPacedTime = + std::max(kPacketsToSend * kPacketSize / kPacingDataRate, + kPacketsToSend * kPacketSize / kProbingRate); + + // Expect all packets and one padding packet sent. + EXPECT_CALL(packet_router, SendPacket).Times(kPacketsToSend + 1); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + time_controller.AdvanceTime(kPacketsPacedTime + TimeDelta::Millis(1)); +} + TEST(TaskQueuePacedSenderTest, Stats) { static constexpr Timestamp kStartTime = Timestamp::Millis(1234); GlobalSimulatedTimeController time_controller(kStartTime); MockPacketRouter packet_router; - TaskQueuePacedSender pacer( - time_controller.GetClock(), &packet_router, - /*event_log=*/nullptr, - /*field_trials=*/nullptr, time_controller.GetTaskQueueFactory(), - PacingController::kMinSleepTime, kNoPacketHoldback); + ScopedKeyValueConfig trials; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, trials, + time_controller.GetTaskQueueFactory(), + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); // Simulate ~2mbps video stream, covering one second. static constexpr size_t kPacketsToSend = 200; @@ -597,5 +725,93 @@ TEST(TaskQueuePacedSenderTest, Stats) { EXPECT_TRUE(pacer.ExpectedQueueTime().IsZero()); } +TEST(TaskQueuePacedSenderTest, HighPrecisionPacingWhenSlackIsDisabled) { + test::ScopedKeyValueConfig experiments( + "WebRTC-SlackedTaskQueuePacedSender/Disabled/"); + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + TaskQueueWithFakePrecisionFactory task_queue_factory( + time_controller.GetTaskQueueFactory()); + + MockPacketRouter packet_router; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, + experiments, &task_queue_factory, + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); + + // Send enough packets (covering one second) that pacing is triggered, i.e. + // delayed tasks being scheduled. + static constexpr size_t kPacketsToSend = 42; + static constexpr DataRate kPacingRate = + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + // Expect all of them to be sent. + size_t packets_sent = 0; + EXPECT_CALL(packet_router, SendPacket) + .WillRepeatedly( + [&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { ++packets_sent; }); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(packets_sent, kPacketsToSend); + + // Expect pacing to make use of high precision. + EXPECT_EQ(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); + + // Create probe cluster which is also high precision. + pacer.CreateProbeCluster(kPacingRate, 123); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); +} + +TEST(TaskQueuePacedSenderTest, LowPrecisionPacingWhenSlackIsEnabled) { + test::ScopedKeyValueConfig experiments( + "WebRTC-SlackedTaskQueuePacedSender/Enabled/"); + + GlobalSimulatedTimeController time_controller(Timestamp::Millis(1234)); + TaskQueueWithFakePrecisionFactory task_queue_factory( + time_controller.GetTaskQueueFactory()); + + MockPacketRouter packet_router; + TaskQueuePacedSender pacer(time_controller.GetClock(), &packet_router, + experiments, &task_queue_factory, + PacingController::kMinSleepTime, + TaskQueuePacedSender::kNoPacketHoldback); + + // Send enough packets (covering one second) that pacing is triggered, i.e. + // delayed tasks being scheduled. + static constexpr size_t kPacketsToSend = 42; + static constexpr DataRate kPacingRate = + DataRate::BitsPerSec(kDefaultPacketSize * 8 * kPacketsToSend); + pacer.SetPacingRates(kPacingRate, DataRate::Zero()); + pacer.EnsureStarted(); + pacer.EnqueuePackets( + GeneratePackets(RtpPacketMediaType::kVideo, kPacketsToSend)); + // Expect all of them to be sent. + size_t packets_sent = 0; + EXPECT_CALL(packet_router, SendPacket) + .WillRepeatedly( + [&](std::unique_ptr packet, + const PacedPacketInfo& cluster_info) { ++packets_sent; }); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_EQ(packets_sent, kPacketsToSend); + + // Expect pacing to make use of low precision. + EXPECT_GT(task_queue_factory.delayed_low_precision_count(), 0); + EXPECT_EQ(task_queue_factory.delayed_high_precision_count(), 0); + + // Create probe cluster, which uses high precision despite regular pacing + // being low precision. + pacer.CreateProbeCluster(kPacingRate, 123); + pacer.EnqueuePackets(GeneratePackets(RtpPacketMediaType::kVideo, 1)); + time_controller.AdvanceTime(TimeDelta::Seconds(1)); + EXPECT_GT(task_queue_factory.delayed_high_precision_count(), 0); +} + } // namespace test } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/BUILD.gn b/modules/remote_bitrate_estimator/BUILD.gn index 923f00a74c..3583e81c66 100644 --- a/modules/remote_bitrate_estimator/BUILD.gn +++ b/modules/remote_bitrate_estimator/BUILD.gn @@ -41,11 +41,11 @@ rtc_library("remote_bitrate_estimator") { } deps = [ + "../../api:field_trials_view", "../../api:network_state_predictor_api", "../../api:rtp_headers", "../../api/transport:field_trial_based_config", "../../api/transport:network_control", - "../../api/transport:webrtc_key_value_config", "../../api/units:data_rate", "../../api/units:data_size", "../../api/units:time_delta", @@ -55,9 +55,14 @@ rtc_library("remote_bitrate_estimator") { "../../modules/congestion_controller/goog_cc:link_capacity_estimator", "../../modules/rtp_rtcp:rtp_rtcp_format", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:race_checker", + "../../rtc_base:rate_statistics", "../../rtc_base:rtc_numerics", "../../rtc_base:safe_minmax", + "../../rtc_base:stringutils", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/synchronization:mutex", "../../system_wrappers", @@ -78,7 +83,6 @@ if (!build_with_chromium) { "tools/bwe_rtp.h", ] deps = [ - "../../rtc_base:rtc_base_approved", "../../test:rtp_test_utils", "../rtp_rtcp:rtp_rtcp_format", ] @@ -122,9 +126,10 @@ if (rtc_include_tests) { "../../api/transport:field_trial_based_config", "../../api/transport:mock_network_control", "../../api/transport:network_control", + "../../api/units:data_rate", "../../rtc_base", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", "../../system_wrappers", "../../test:field_trial", "../../test:fileutils", diff --git a/modules/remote_bitrate_estimator/DEPS b/modules/remote_bitrate_estimator/DEPS index 66a3201bd0..35a62119e5 100644 --- a/modules/remote_bitrate_estimator/DEPS +++ b/modules/remote_bitrate_estimator/DEPS @@ -1,6 +1,6 @@ include_rules = [ "+logging/rtc_event_log", "+system_wrappers", - # Avoid directly using field_trial. Instead use WebRtcKeyValueConfig. + # Avoid directly using field_trial. Instead use FieldTrialsView. "-system_wrappers/include/field_trial.h", ] diff --git a/modules/remote_bitrate_estimator/OWNERS b/modules/remote_bitrate_estimator/OWNERS index 9b97144ac8..97e5d51dac 100644 --- a/modules/remote_bitrate_estimator/OWNERS +++ b/modules/remote_bitrate_estimator/OWNERS @@ -3,4 +3,4 @@ terelius@webrtc.org asapersson@webrtc.org mflodman@webrtc.org philipel@webrtc.org -srte@webrtc.org +perkj@webrtc.org diff --git a/modules/remote_bitrate_estimator/aimd_rate_control.cc b/modules/remote_bitrate_estimator/aimd_rate_control.cc index a3da2b5f2d..8f9c33c7f6 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control.cc +++ b/modules/remote_bitrate_estimator/aimd_rate_control.cc @@ -35,17 +35,15 @@ constexpr double kDefaultBackoffFactor = 0.85; constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor"; -bool IsEnabled(const WebRtcKeyValueConfig& field_trials, - absl::string_view key) { +bool IsEnabled(const FieldTrialsView& field_trials, absl::string_view key) { return absl::StartsWith(field_trials.Lookup(key), "Enabled"); } -bool IsNotDisabled(const WebRtcKeyValueConfig& field_trials, - absl::string_view key) { +bool IsNotDisabled(const FieldTrialsView& field_trials, absl::string_view key) { return !absl::StartsWith(field_trials.Lookup(key), "Disabled"); } -double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) { +double ReadBackoffFactor(const FieldTrialsView& key_value_config) { std::string experiment_string = key_value_config.Lookup(kBweBackOffFactorExperiment); double backoff_factor; @@ -67,10 +65,10 @@ double ReadBackoffFactor(const WebRtcKeyValueConfig& key_value_config) { } // namespace -AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config) +AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config) : AimdRateControl(key_value_config, /* send_side =*/false) {} -AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config, +AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config, bool send_side) : min_configured_bitrate_(congestion_controller::GetMinBitrate()), max_configured_bitrate_(DataRate::KilobitsPerSec(30000)), @@ -95,11 +93,13 @@ AimdRateControl::AimdRateControl(const WebRtcKeyValueConfig* key_value_config, estimate_bounded_backoff_( IsNotDisabled(*key_value_config, "WebRTC-Bwe-EstimateBoundedBackoff")), - estimate_bounded_increase_( - IsNotDisabled(*key_value_config, - "WebRTC-Bwe-EstimateBoundedIncrease")), initial_backoff_interval_("initial_backoff_interval"), link_capacity_fix_("link_capacity_fix") { + ParseFieldTrial( + {&disable_estimate_bounded_increase_, &estimate_bounded_increase_ratio_, + &ignore_throughput_limit_if_network_estimate_, + &ignore_network_estimate_decrease_, &increase_to_network_estimate_}, + key_value_config->Lookup("WebRTC-Bwe-EstimateBoundedIncrease")); // E.g // WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/ ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_}, @@ -272,29 +272,39 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, ChangeState(input, at_time); - // We limit the new bitrate based on the troughput to avoid unlimited bitrate - // increases. We allow a bit more lag at very low rates to not too easily get - // stuck if the encoder produces uneven outputs. - const DataRate troughput_based_limit = - 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); - switch (rate_control_state_) { case RateControlState::kRcHold: break; - case RateControlState::kRcIncrease: + case RateControlState::kRcIncrease: { if (estimated_throughput > link_capacity_.UpperBound()) link_capacity_.Reset(); - // Do not increase the delay based estimate in alr since the estimator - // will not be able to get transport feedback necessary to detect if - // the new estimate is correct. - // If we have previously increased above the limit (for instance due to - // probing), we don't allow further changes. - if (current_bitrate_ < troughput_based_limit && - !(send_side_ && in_alr_ && no_bitrate_increase_in_alr_)) { + // We limit the new bitrate based on the troughput to avoid unlimited + // bitrate increases. We allow a bit more lag at very low rates to not too + // easily get stuck if the encoder produces uneven outputs. + DataRate increase_limit = + 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); + if (ignore_throughput_limit_if_network_estimate_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + // If we have a Network estimate, we do allow the estimate to increase. + increase_limit = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + } else if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) { + // Do not increase the delay based estimate in alr since the estimator + // will not be able to get transport feedback necessary to detect if + // the new estimate is correct. + // If we have previously increased above the limit (for instance due to + // probing), we don't allow further changes. + increase_limit = current_bitrate_; + } + + if (current_bitrate_ < increase_limit) { DataRate increased_bitrate = DataRate::MinusInfinity(); - if (link_capacity_.has_estimate()) { + if (increase_to_network_estimate_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + increased_bitrate = increase_limit; + } else if (link_capacity_.has_estimate()) { // The link_capacity estimate is reset if the measured throughput // is too far from the estimate. We can therefore assume that our // target rate is reasonably close to link capacity and use additive @@ -309,11 +319,11 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, at_time, time_last_bitrate_change_, current_bitrate_); increased_bitrate = current_bitrate_ + multiplicative_increase; } - new_bitrate = std::min(increased_bitrate, troughput_based_limit); + new_bitrate = std::min(increased_bitrate, increase_limit); } - time_last_bitrate_change_ = at_time; break; + } case RateControlState::kRcDecrease: { DataRate decreased_bitrate = DataRate::PlusInfinity(); @@ -329,11 +339,6 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, decreased_bitrate = beta_ * link_capacity_.estimate(); } } - if (estimate_bounded_backoff_ && network_estimate_) { - decreased_bitrate = std::max( - decreased_bitrate, network_estimate_->link_capacity_lower * beta_); - } - // Avoid increasing the rate when over-using. if (decreased_bitrate < current_bitrate_) { new_bitrate = decreased_bitrate; @@ -368,9 +373,21 @@ void AimdRateControl::ChangeBitrate(const RateControlInput& input, } DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const { - if (estimate_bounded_increase_ && network_estimate_) { - DataRate upper_bound = network_estimate_->link_capacity_upper; - new_bitrate = std::min(new_bitrate, upper_bound); + if (!disable_estimate_bounded_increase_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + DataRate upper_bound = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + if (ignore_network_estimate_decrease_) { + upper_bound = std::max(upper_bound, current_bitrate_); + } + new_bitrate = std::min(upper_bound, new_bitrate); + } + if (estimate_bounded_backoff_ && network_estimate_ && + network_estimate_->link_capacity_lower.IsFinite() && + new_bitrate < current_bitrate_) { + new_bitrate = std::min( + current_bitrate_, + std::max(new_bitrate, network_estimate_->link_capacity_lower * beta_)); } new_bitrate = std::max(new_bitrate, min_configured_bitrate_); return new_bitrate; diff --git a/modules/remote_bitrate_estimator/aimd_rate_control.h b/modules/remote_bitrate_estimator/aimd_rate_control.h index 3e0d541b60..6c770cdc45 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control.h +++ b/modules/remote_bitrate_estimator/aimd_rate_control.h @@ -14,8 +14,8 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/transport/network_types.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/units/data_rate.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/goog_cc/link_capacity_estimator.h" @@ -30,8 +30,8 @@ namespace webrtc { // multiplicatively. class AimdRateControl { public: - explicit AimdRateControl(const WebRtcKeyValueConfig* key_value_config); - AimdRateControl(const WebRtcKeyValueConfig* key_value_config, bool send_side); + explicit AimdRateControl(const FieldTrialsView* key_value_config); + AimdRateControl(const FieldTrialsView* key_value_config, bool send_side); ~AimdRateControl(); // Returns true if the target bitrate has been initialized. This happens @@ -107,9 +107,16 @@ class AimdRateControl { // Use estimated link capacity lower bound if it is higher than the // acknowledged rate when backing off due to overuse. const bool estimate_bounded_backoff_; - // Use estimated link capacity upper bound as upper limit for increasing - // bitrate over the acknowledged rate. - const bool estimate_bounded_increase_; + // If false, uses estimated link capacity upper bound * + // `estimate_bounded_increase_ratio_` as upper limit for the estimate. + FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"}; + FieldTrialParameter estimate_bounded_increase_ratio_{"ratio", 1.0}; + FieldTrialParameter ignore_throughput_limit_if_network_estimate_{ + "ignore_acked", false}; + FieldTrialParameter increase_to_network_estimate_{"immediate_incr", + false}; + FieldTrialParameter ignore_network_estimate_decrease_{"ignore_decr", + false}; absl::optional last_decrease_; FieldTrialOptional initial_backoff_interval_; FieldTrialFlag link_capacity_fix_; diff --git a/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc b/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc index 6cbccf6b7b..aa80dae55b 100644 --- a/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc +++ b/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc @@ -12,6 +12,7 @@ #include #include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" #include "system_wrappers/include/clock.h" #include "test/field_trial.h" #include "test/gtest.h" @@ -254,6 +255,62 @@ TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) { 2 * kInitialBitrateBps); } +TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 100'000); + // 0.85 is default backoff factor. (`beta_`) + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_lower * 0.85); +} + +TEST(AimdRateControlTest, + SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + SetEstimate(states, 200'000); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + // Ignore the next SetEstimate, since the estimate is lower than 85% of + // the network estimate. + SetEstimate(states, 100'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); +} + +TEST(AimdRateControlTest, SetEstimateIgnoresNetworkEstimatesLowerThanCurrent) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(30)); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); + + NetworkStateEstimate lower_network_estimate; + lower_network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(300); + states.aimd_rate_control->SetNetworkStateEstimate(lower_network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { // Allow the estimate to increase as long as alr is not detected to ensure // tha BWE can not get stuck at a certain bitrate. @@ -274,4 +331,154 @@ TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { kInitialBitrateBps); } +TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(false); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_GT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, + EstimateSlowlyIncreaseToUpperLinkCapacityEstimateIfConfigured) { + // Even if alr is detected, the delay based estimator is allowed to increase + // up to a percentage of upper link capacity. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,immediate_incr:false/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 10; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + EXPECT_LT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); + } + for (int i = 0; i < 50; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, + EstimateImmediatelyIncreaseToUpperLinkCapacityEstimateIfConfigured) { + // Even if alr is detected, the delay based estimator is allowed to increase + // up to a percentage of upper link capacity. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,immediate_incr:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, EstimateNotLoweredByNetworkEstimate) { + // The delay based estimator is allowed to increase up to a percentage of + // upper link capacity but does not decrease unless the delay detector + // discover an overuse. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + constexpr int kEstimatedThroughputBps = 30'000; + SetEstimate(states, kInitialBitrateBps); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + DataRate estimate_after_increase = states.aimd_rate_control->LatestEstimate(); + ASSERT_EQ(estimate_after_increase, + network_estimate.link_capacity_upper * 0.85); + + // A lower network estimate does not decrease the estimate immediately, + // but the estimate is not allowed to increase. + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(100); + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(80); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 10; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + estimate_after_increase); + } + + // If the detector detects and overuse, BWE drops to a value relative the + // network estimate. + UpdateRateControl(states, BandwidthUsage::kBwOverusing, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_LT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_lower); + EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), + kEstimatedThroughputBps); +} + +TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlrIfNetworkEstimateNotSet) { + // When alr is detected, the delay based estimator is not allowed to increase + // bwe since there will be no feedback from the network if the new estimate + // is correct. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); +} + } // namespace webrtc diff --git a/modules/remote_bitrate_estimator/bwe_defines.cc b/modules/remote_bitrate_estimator/bwe_defines.cc index 6afbe133e2..22c605e82d 100644 --- a/modules/remote_bitrate_estimator/bwe_defines.cc +++ b/modules/remote_bitrate_estimator/bwe_defines.cc @@ -10,8 +10,6 @@ #include "modules/remote_bitrate_estimator/include/bwe_defines.h" -#include "system_wrappers/include/field_trial.h" - namespace webrtc { const char kBweTypeHistogram[] = "WebRTC.BWE.Types"; diff --git a/modules/remote_bitrate_estimator/overuse_detector.cc b/modules/remote_bitrate_estimator/overuse_detector.cc index 710b3b21d3..672822bbcd 100644 --- a/modules/remote_bitrate_estimator/overuse_detector.cc +++ b/modules/remote_bitrate_estimator/overuse_detector.cc @@ -33,7 +33,7 @@ const double kOverUsingTimeThreshold = 10; const int kMaxNumDeltas = 60; bool AdaptiveThresholdExperimentIsDisabled( - const WebRtcKeyValueConfig& key_value_config) { + const FieldTrialsView& key_value_config) { std::string experiment_string = key_value_config.Lookup(kAdaptiveThresholdExperiment); const size_t kMinExperimentLength = kDisabledPrefixLength; @@ -44,7 +44,7 @@ bool AdaptiveThresholdExperimentIsDisabled( // Gets thresholds from the experiment name following the format // "WebRTC-AdaptiveBweThreshold/Enabled-0.5,0.002/". -bool ReadExperimentConstants(const WebRtcKeyValueConfig& key_value_config, +bool ReadExperimentConstants(const FieldTrialsView& key_value_config, double* k_up, double* k_down) { std::string experiment_string = @@ -57,7 +57,7 @@ bool ReadExperimentConstants(const WebRtcKeyValueConfig& key_value_config, "%lf,%lf", k_up, k_down) == 2; } -OveruseDetector::OveruseDetector(const WebRtcKeyValueConfig* key_value_config) +OveruseDetector::OveruseDetector(const FieldTrialsView* key_value_config) // Experiment is on by default, but can be disabled with finch by setting // the field trial string to "WebRTC-AdaptiveBweThreshold/Disabled/". : in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)), @@ -147,7 +147,7 @@ void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) { } void OveruseDetector::InitializeExperiment( - const WebRtcKeyValueConfig& key_value_config) { + const FieldTrialsView& key_value_config) { RTC_DCHECK(in_experiment_); double k_up = 0.0; double k_down = 0.0; diff --git a/modules/remote_bitrate_estimator/overuse_detector.h b/modules/remote_bitrate_estimator/overuse_detector.h index 179e290c21..dfaea9187a 100644 --- a/modules/remote_bitrate_estimator/overuse_detector.h +++ b/modules/remote_bitrate_estimator/overuse_detector.h @@ -12,17 +12,17 @@ #include +#include "api/field_trials_view.h" #include "api/network_state_predictor.h" -#include "api/transport/webrtc_key_value_config.h" namespace webrtc { bool AdaptiveThresholdExperimentIsDisabled( - const WebRtcKeyValueConfig& key_value_config); + const FieldTrialsView& key_value_config); class OveruseDetector { public: - explicit OveruseDetector(const WebRtcKeyValueConfig* key_value_config); + explicit OveruseDetector(const FieldTrialsView* key_value_config); virtual ~OveruseDetector(); OveruseDetector(const OveruseDetector&) = delete; @@ -44,7 +44,7 @@ class OveruseDetector { private: void UpdateThreshold(double modified_offset, int64_t now_ms); - void InitializeExperiment(const WebRtcKeyValueConfig& key_value_config); + void InitializeExperiment(const FieldTrialsView& key_value_config); bool in_experiment_; double k_up_; diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc index c1c56977cc..028d0db46e 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc @@ -44,7 +44,7 @@ struct RemoteBitrateEstimatorSingleStream::Detector { explicit Detector(int64_t last_packet_time_ms, const OverUseDetectorOptions& options, bool enable_burst_grouping, - const WebRtcKeyValueConfig* key_value_config) + const FieldTrialsView* key_value_config) : last_packet_time_ms(last_packet_time_ms), inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs, diff --git a/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc b/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc index d1675bd805..b70ae8533f 100644 --- a/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc +++ b/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc @@ -179,7 +179,6 @@ int64_t StreamGenerator::GenerateFrame(RtpStream::PacketList* packets, StreamMap::iterator it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); (*it).second->GenerateFrame(time_now_us, packets); - int i = 0; for (RtpStream::PacketList::iterator packet_it = packets->begin(); packet_it != packets->end(); ++packet_it) { int capacity_bpus = capacity_ / 1000; @@ -189,7 +188,6 @@ int64_t StreamGenerator::GenerateFrame(RtpStream::PacketList* packets, std::max(time_now_us + required_network_time_us, prev_arrival_time_us_ + required_network_time_us); (*packet_it)->arrival_time = prev_arrival_time_us_; - ++i; } it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); return std::max((*it).second->next_rtp_time(), time_now_us); diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc index 710736876e..fd5f629235 100644 --- a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc +++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc @@ -31,7 +31,7 @@ static constexpr int64_t kMaxTimeMs = RemoteEstimatorProxy::RemoteEstimatorProxy( Clock* clock, TransportFeedbackSender feedback_sender, - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, NetworkStateEstimator* network_state_estimator) : clock_(clock), feedback_sender_(std::move(feedback_sender)), diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.h b/modules/remote_bitrate_estimator/remote_estimator_proxy.h index 4e9b2b5631..438aa94dc5 100644 --- a/modules/remote_bitrate_estimator/remote_estimator_proxy.h +++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.h @@ -16,8 +16,8 @@ #include #include +#include "api/field_trials_view.h" #include "api/transport/network_control.h" -#include "api/transport/webrtc_key_value_config.h" #include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" #include "modules/remote_bitrate_estimator/packet_arrival_map.h" #include "rtc_base/experiments/field_trial_parser.h" @@ -42,7 +42,7 @@ class RemoteEstimatorProxy : public RemoteBitrateEstimator { std::vector> packets)>; RemoteEstimatorProxy(Clock* clock, TransportFeedbackSender feedback_sender, - const WebRtcKeyValueConfig* key_value_config, + const FieldTrialsView* key_value_config, NetworkStateEstimator* network_state_estimator); ~RemoteEstimatorProxy() override; @@ -68,7 +68,7 @@ class RemoteEstimatorProxy : public RemoteBitrateEstimator { TimeDelta::Millis(100)}; FieldTrialParameter bandwidth_fraction{"frac", 0.05}; explicit TransportWideFeedbackConfig( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { ParseFieldTrial({&back_window, &min_interval, &max_interval, &default_interval, &bandwidth_fraction}, key_value_config->Lookup( diff --git a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc index f99576f59a..c8f6faa127 100644 --- a/modules/remote_bitrate_estimator/test/bwe_test_logging.cc +++ b/modules/remote_bitrate_estimator/test/bwe_test_logging.cc @@ -12,13 +12,13 @@ #if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE +#include #include #include #include #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/platform_thread.h" #include "rtc_base/strings/string_builder.h" diff --git a/modules/remote_bitrate_estimator/tools/rtp_to_text.cc b/modules/remote_bitrate_estimator/tools/rtp_to_text.cc index 98f502a42e..e8dc59f740 100644 --- a/modules/remote_bitrate_estimator/tools/rtp_to_text.cc +++ b/modules/remote_bitrate_estimator/tools/rtp_to_text.cc @@ -16,7 +16,6 @@ #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_packet.h" -#include "rtc_base/format_macros.h" #include "rtc_base/strings/string_builder.h" #include "test/rtp_file_reader.h" @@ -52,10 +51,10 @@ int main(int argc, char* argv[]) { ss << static_cast(packet.time_ms) * 1000000; fprintf(stdout, "%s\n", ss.str().c_str()); } else { - fprintf(stdout, "%u %u %d %u %u %d %u %" RTC_PRIuS " %" RTC_PRIuS "\n", - header.SequenceNumber(), header.Timestamp(), toffset, - abs_send_time, packet.time_ms, header.Marker(), header.Ssrc(), - packet.length, packet.original_length); + fprintf(stdout, "%u %u %d %u %u %d %u %zu %zu\n", header.SequenceNumber(), + header.Timestamp(), toffset, abs_send_time, packet.time_ms, + header.Marker(), header.Ssrc(), packet.length, + packet.original_length); } ++packet_counter; } diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 9922484647..8e25b42a46 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -118,10 +118,17 @@ rtc_library("rtp_rtcp_format") { "../../api/video:video_layers_allocation", "../../api/video:video_rtp_headers", "../../common_video", + "../../rtc_base:bit_buffer", "../../rtc_base:bitstream_reader", + "../../rtc_base:buffer", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", "../../rtc_base:divide_round", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:safe_conversions", + "../../rtc_base:stringutils", "../../system_wrappers", "../video_coding:codec_globals_headers", ] @@ -253,6 +260,7 @@ rtc_library("rtp_rtcp") { "..:module_api_public", "..:module_fec_api", "../../api:array_view", + "../../api:field_trials_view", "../../api:frame_transformer_interface", "../../api:function_view", "../../api:libjingle_peerconnection_api", @@ -267,7 +275,6 @@ rtc_library("rtp_rtcp") { "../../api/rtc_event_log", "../../api/task_queue:task_queue", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../api/transport/rtp:dependency_descriptor", "../../api/transport/rtp:rtp_source", "../../api/units:data_rate", @@ -288,14 +295,27 @@ rtc_library("rtp_rtcp") { "../../logging:rtc_event_audio", "../../logging:rtc_event_rtp_rtcp", "../../modules/audio_coding:audio_coding_module_typedefs", + "../../rtc_base:bit_buffer", "../../rtc_base:bitstream_reader", + "../../rtc_base:buffer", + "../../rtc_base:byte_buffer", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", "../../rtc_base:divide_round", + "../../rtc_base:event_tracer", "../../rtc_base:gtest_prod", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:mod_ops", + "../../rtc_base:one_time_event", + "../../rtc_base:race_checker", + "../../rtc_base:random", "../../rtc_base:rate_limiter", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:rate_statistics", "../../rtc_base:rtc_numerics", + "../../rtc_base:safe_conversions", "../../rtc_base:safe_minmax", + "../../rtc_base:timeutils", "../../rtc_base/containers:flat_map", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/synchronization:mutex", @@ -342,7 +362,9 @@ rtc_source_set("rtp_rtcp_legacy") { "../../logging:rtc_event_rtp_rtcp", "../../rtc_base:checks", "../../rtc_base:gtest_prod", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:rate_statistics", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../remote_bitrate_estimator", @@ -377,8 +399,11 @@ rtc_library("rtcp_transceiver") { "../../api/units:timestamp", "../../api/video:video_bitrate_allocation", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", "../../rtc_base:divide_round", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:rtc_event", + "../../rtc_base:timeutils", "../../rtc_base/containers:flat_map", "../../rtc_base/task_utils:repeating_task", "../../rtc_base/task_utils:to_queued_task", @@ -423,7 +448,7 @@ rtc_library("fec_test_helper") { ":rtp_rtcp", ":rtp_rtcp_format", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", ] } @@ -440,7 +465,6 @@ rtc_library("mock_rtp_rtcp") { ":rtp_rtcp_format", "../../api/video:video_bitrate_allocation", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../test:test_support", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -481,7 +505,7 @@ if (rtc_include_tests) { deps = [ ":rtp_rtcp", ":rtp_rtcp_format", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:random", "../../test:fileutils", "../../test:test_support", ] @@ -606,12 +630,20 @@ if (rtc_include_tests) { "../../common_video/generic_frame_descriptor", "../../common_video/test:utilities", "../../logging:mocks", + "../../rtc_base:bit_buffer", + "../../rtc_base:buffer", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:random", "../../rtc_base:rate_limiter", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", + "../../rtc_base:timeutils", "../../rtc_base/task_utils:to_queued_task", "../../system_wrappers", "../../test:field_trial", diff --git a/modules/rtp_rtcp/DEPS b/modules/rtp_rtcp/DEPS index dac95dd23a..3eec1ca90d 100644 --- a/modules/rtp_rtcp/DEPS +++ b/modules/rtp_rtcp/DEPS @@ -3,6 +3,6 @@ include_rules = [ "+common_video", "+logging/rtc_event_log", "+system_wrappers", - # Avoid directly using field_trial. Instead use WebRtcKeyValueConfig. + # Avoid directly using field_trial. Instead use FieldTrialsView. "-system_wrappers/include/field_trial.h", ] diff --git a/modules/rtp_rtcp/include/remote_ntp_time_estimator.h b/modules/rtp_rtcp/include/remote_ntp_time_estimator.h index f31503dc4e..9abad381af 100644 --- a/modules/rtp_rtcp/include/remote_ntp_time_estimator.h +++ b/modules/rtp_rtcp/include/remote_ntp_time_estimator.h @@ -13,7 +13,10 @@ #include +#include "absl/base/attributes.h" #include "absl/types/optional.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "rtc_base/numerics/moving_median_filter.h" #include "system_wrappers/include/rtp_to_ntp_estimator.h" @@ -28,32 +31,64 @@ class Clock; class RemoteNtpTimeEstimator { public: explicit RemoteNtpTimeEstimator(Clock* clock); - - ~RemoteNtpTimeEstimator(); - RemoteNtpTimeEstimator(const RemoteNtpTimeEstimator&) = delete; RemoteNtpTimeEstimator& operator=(const RemoteNtpTimeEstimator&) = delete; + ~RemoteNtpTimeEstimator() = default; // Updates the estimator with round trip time `rtt`, NTP seconds `ntp_secs`, // NTP fraction `ntp_frac` and RTP timestamp `rtp_timestamp`. + ABSL_DEPRECATED( + "Use UpdateRtcpTimestamp with strict time types: TimeDelta and NtpTime.") bool UpdateRtcpTimestamp(int64_t rtt, uint32_t ntp_secs, uint32_t ntp_frac, + uint32_t rtp_timestamp) { + return UpdateRtcpTimestamp(TimeDelta::Millis(rtt), + NtpTime(ntp_secs, ntp_frac), rtp_timestamp); + } + + bool UpdateRtcpTimestamp(TimeDelta rtt, + NtpTime sender_send_time, uint32_t rtp_timestamp); // Estimates the NTP timestamp in local timebase from `rtp_timestamp`. // Returns the NTP timestamp in ms when success. -1 if failed. - int64_t Estimate(uint32_t rtp_timestamp); + int64_t Estimate(uint32_t rtp_timestamp) { + NtpTime ntp_time = EstimateNtp(rtp_timestamp); + if (!ntp_time.Valid()) { + return -1; + } + return ntp_time.ToMs(); + } + + // Estimates the NTP timestamp in local timebase from `rtp_timestamp`. + // Returns invalid NtpTime (i.e. NtpTime(0)) on failure. + NtpTime EstimateNtp(uint32_t rtp_timestamp); // Estimates the offset, in milliseconds, between the remote clock and the // local one. This is equal to local NTP clock - remote NTP clock. - absl::optional EstimateRemoteToLocalClockOffsetMs(); + ABSL_DEPRECATED("Use EstimateRemoteToLocalClockOffset.") + absl::optional EstimateRemoteToLocalClockOffsetMs() { + if (absl::optional offset = EstimateRemoteToLocalClockOffset()) { + return std::round(static_cast(*offset) * 1'000 / + (int64_t{1} << 32)); + } + return absl::nullopt; + } + + // Estimates the offset between the remote clock and the + // local one. This is equal to local NTP clock - remote NTP clock. + // The offset is returned in ntp time resolution, i.e. 1/2^32 sec ~= 0.2 ns. + // Returns nullopt on failure. + absl::optional EstimateRemoteToLocalClockOffset(); private: Clock* clock_; + // Offset is measured with the same precision as NtpTime: in 1/2^32 seconds ~= + // 0.2 ns. MovingMedianFilter ntp_clocks_offset_estimator_; RtpToNtpEstimator rtp_to_ntp_; - int64_t last_timing_log_ms_; + Timestamp last_timing_log_ = Timestamp::MinusInfinity(); }; } // namespace webrtc 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 c5c06840d2..35936c41ee 100644 --- a/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc +++ b/modules/rtp_rtcp/source/deprecated/deprecated_rtp_sender_egress.cc @@ -27,8 +27,7 @@ constexpr int kSendSideDelayWindowMs = 1000; constexpr int kBitrateStatisticsWindowMs = 1000; constexpr size_t kRtpSequenceNumberMapMaxEntries = 1 << 13; -bool IsDisabled(absl::string_view name, - const WebRtcKeyValueConfig* field_trials) { +bool IsDisabled(absl::string_view name, const FieldTrialsView* field_trials) { FieldTrialBasedConfig default_trials; auto& trials = field_trials ? *field_trials : default_trials; return absl::StartsWith(trials.Lookup(name), "Disabled"); @@ -156,7 +155,7 @@ void DEPRECATED_RtpSenderEgress::SendPacket( // In case of VideoTimingExtension, since it's present not in every packet, // data after rtp header may be corrupted if these packets are protected by // the FEC. - int64_t diff_ms = now_ms - packet->capture_time_ms(); + int64_t diff_ms = now_ms - packet->capture_time().ms(); if (packet->HasExtension()) { packet->SetExtension(kTimestampTicksPerMs * diff_ms); } @@ -167,9 +166,9 @@ void DEPRECATED_RtpSenderEgress::SendPacket( if (packet->HasExtension()) { if (populate_network2_timestamp_) { - packet->set_network2_time_ms(now_ms); + packet->set_network2_time(Timestamp::Millis(now_ms)); } else { - packet->set_pacer_exit_time_ms(now_ms); + packet->set_pacer_exit_time(Timestamp::Millis(now_ms)); } } @@ -190,8 +189,8 @@ void DEPRECATED_RtpSenderEgress::SendPacket( if (packet->packet_type() != RtpPacketMediaType::kPadding && packet->packet_type() != RtpPacketMediaType::kRetransmission) { - UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc); - UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), + UpdateDelayStatistics(packet->capture_time().ms(), now_ms, packet_ssrc); + UpdateOnSendPacket(options.packet_id, packet->capture_time().ms(), packet_ssrc); } @@ -201,7 +200,7 @@ void DEPRECATED_RtpSenderEgress::SendPacket( // actual sending fails. if (is_media && packet->allow_retransmission()) { packet_history_->PutRtpPacket(std::make_unique(*packet), - now_ms); + Timestamp::Millis(now_ms)); } else if (packet->retransmitted_sequence_number()) { packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number()); } diff --git a/modules/rtp_rtcp/source/flexfec_receiver.cc b/modules/rtp_rtcp/source/flexfec_receiver.cc index e01b9205cf..b5198b53a6 100644 --- a/modules/rtp_rtcp/source/flexfec_receiver.cc +++ b/modules/rtp_rtcp/source/flexfec_receiver.cc @@ -159,18 +159,28 @@ void FlexfecReceiver::ProcessReceivedPacket( // Set this flag first, since OnRecoveredPacket may end up here // again, with the same packet. recovered_packet->returned = true; - RTC_CHECK_GT(recovered_packet->pkt->data.size(), 0); + RTC_CHECK_GE(recovered_packet->pkt->data.size(), kRtpHeaderSize); recovered_packet_receiver_->OnRecoveredPacket( recovered_packet->pkt->data.cdata(), recovered_packet->pkt->data.size()); - // Periodically log the incoming packets. + uint32_t media_ssrc = + ForwardErrorCorrection::ParseSsrc(recovered_packet->pkt->data.data()); + uint16_t media_seq_num = ForwardErrorCorrection::ParseSequenceNumber( + recovered_packet->pkt->data.data()); + // Periodically log the incoming packets at LS_INFO. int64_t now_ms = clock_->TimeInMilliseconds(); - if (now_ms - last_recovered_packet_ms_ > kPacketLogIntervalMs) { - uint32_t media_ssrc = - ForwardErrorCorrection::ParseSsrc(recovered_packet->pkt->data.data()); - RTC_LOG(LS_VERBOSE) << "Recovered media packet with SSRC: " << media_ssrc - << " from FlexFEC stream with SSRC: " << ssrc_ << "."; - last_recovered_packet_ms_ = now_ms; + bool should_log_periodically = + now_ms - last_recovered_packet_ms_ > kPacketLogIntervalMs; + if (RTC_LOG_CHECK_LEVEL(LS_VERBOSE) || should_log_periodically) { + rtc::LoggingSeverity level = + should_log_periodically ? rtc::LS_INFO : rtc::LS_VERBOSE; + RTC_LOG_V(level) << "Recovered media packet with SSRC: " << media_ssrc + << " seq " << media_seq_num << " recovered length " + << recovered_packet->pkt->data.size() + << " from FlexFEC stream with SSRC: " << ssrc_; + if (should_log_periodically) { + last_recovered_packet_ms_ = now_ms; + } } } } diff --git a/modules/rtp_rtcp/source/flexfec_sender.cc b/modules/rtp_rtcp/source/flexfec_sender.cc index 071829f1c0..f6fe06e0e4 100644 --- a/modules/rtp_rtcp/source/flexfec_sender.cc +++ b/modules/rtp_rtcp/source/flexfec_sender.cc @@ -142,7 +142,7 @@ std::vector> FlexfecSender::GetFecPackets() { clock_->TimeInMilliseconds())); // Set "capture time" so that the TransmissionOffset header extension // can be set by the RTPSender. - fec_packet_to_send->set_capture_time_ms(clock_->TimeInMilliseconds()); + fec_packet_to_send->set_capture_time(clock_->CurrentTime()); fec_packet_to_send->SetSsrc(ssrc_); // Reserve extensions, if registered. These will be set by the RTPSender. fec_packet_to_send->ReserveExtension(); diff --git a/modules/rtp_rtcp/source/forward_error_correction.cc b/modules/rtp_rtcp/source/forward_error_correction.cc index 989fb3d58a..ed6a757a62 100644 --- a/modules/rtp_rtcp/source/forward_error_correction.cc +++ b/modules/rtp_rtcp/source/forward_error_correction.cc @@ -651,10 +651,10 @@ bool ForwardErrorCorrection::RecoverPacket(const ReceivedFecPacket& fec_packet, // This is the packet we're recovering. recovered_packet->seq_num = protected_packet->seq_num; } else { - XorHeaders(*protected_packet->pkt, recovered_packet->pkt); + XorHeaders(*protected_packet->pkt, recovered_packet->pkt.get()); XorPayloads(*protected_packet->pkt, protected_packet->pkt->data.size() - kRtpHeaderSize, - kRtpHeaderSize, recovered_packet->pkt); + kRtpHeaderSize, recovered_packet->pkt.get()); } } if (!FinishPacketRecovery(fec_packet, recovered_packet)) { diff --git a/modules/rtp_rtcp/source/packet_sequencer.cc b/modules/rtp_rtcp/source/packet_sequencer.cc index 037542ddbd..55edd768a8 100644 --- a/modules/rtp_rtcp/source/packet_sequencer.cc +++ b/modules/rtp_rtcp/source/packet_sequencer.cc @@ -99,7 +99,7 @@ void PacketSequencer::UpdateLastPacketState(const RtpPacketToSend& packet) { // 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(); + last_capture_time_ms_ = packet.capture_time().ms(); } void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { @@ -107,7 +107,7 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { RTC_DCHECK(CanSendPaddingOnMediaSsrc()); packet.SetTimestamp(last_rtp_timestamp_); - packet.set_capture_time_ms(last_capture_time_ms_); + packet.set_capture_time(Timestamp::Millis(last_capture_time_ms_)); packet.SetPayloadType(last_payload_type_); return; } @@ -119,7 +119,7 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { } packet.SetTimestamp(last_rtp_timestamp_); - packet.set_capture_time_ms(last_capture_time_ms_); + packet.set_capture_time(Timestamp::Millis(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 @@ -129,9 +129,10 @@ void PacketSequencer::PopulatePaddingFields(RtpPacketToSend& packet) { 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_)); + if (packet.capture_time() > Timestamp::Zero()) { + packet.set_capture_time( + packet.capture_time() + + TimeDelta::Millis(now_ms - last_timestamp_time_ms_)); } } } diff --git a/modules/rtp_rtcp/source/packet_sequencer_unittest.cc b/modules/rtp_rtcp/source/packet_sequencer_unittest.cc index b82e76541c..d892863768 100644 --- a/modules/rtp_rtcp/source/packet_sequencer_unittest.cc +++ b/modules/rtp_rtcp/source/packet_sequencer_unittest.cc @@ -41,10 +41,10 @@ class PacketSequencerTest : public ::testing::Test { packet.set_packet_type(type); packet.SetSsrc(ssrc); packet.SetSequenceNumber(kDefaultSequenceNumber); - packet.set_capture_time_ms(clock_.TimeInMilliseconds()); + packet.set_capture_time(clock_.CurrentTime()); packet.SetTimestamp( kStartRtpTimestamp + - static_cast(packet.capture_time_ms() - kStartTime.ms())); + static_cast(packet.capture_time().ms() - kStartTime.ms())); return packet; } @@ -152,7 +152,7 @@ TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastMediaPacket) { EXPECT_EQ(padding_packet.SequenceNumber(), kMediaStartSequenceNumber + 1); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType); EXPECT_EQ(padding_packet.Timestamp(), media_packet.Timestamp()); - EXPECT_EQ(padding_packet.capture_time_ms(), media_packet.capture_time_ms()); + EXPECT_EQ(padding_packet.capture_time(), media_packet.capture_time()); } TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastRedPacket) { @@ -181,7 +181,7 @@ TEST_F(PacketSequencerTest, UpdatesPaddingBasedOnLastRedPacket) { EXPECT_EQ(padding_packet.SequenceNumber(), kMediaStartSequenceNumber + 1); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType + 1); EXPECT_EQ(padding_packet.Timestamp(), media_packet.Timestamp()); - EXPECT_EQ(padding_packet.capture_time_ms(), media_packet.capture_time_ms()); + EXPECT_EQ(padding_packet.capture_time(), media_packet.capture_time()); } TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { @@ -201,7 +201,7 @@ TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { padding_packet.SetPayloadSize(100); padding_packet.SetPayloadType(kMediaPayloadType + 1); padding_packet.SetTimestamp(kStartRtpTimestamp + 1); - padding_packet.set_capture_time_ms(kStartTime.ms() + 1); + padding_packet.set_capture_time(kStartTime + TimeDelta::Millis(1)); sequencer_.set_rtx_sequence_number(kRtxStartSequenceNumber); sequencer_.Sequence(padding_packet); @@ -209,7 +209,7 @@ TEST_F(PacketSequencerTest, DoesNotUpdateFieldsOnPayloadPadding) { EXPECT_EQ(padding_packet.SequenceNumber(), kRtxStartSequenceNumber); EXPECT_EQ(padding_packet.PayloadType(), kMediaPayloadType + 1); EXPECT_EQ(padding_packet.Timestamp(), kStartRtpTimestamp + 1); - EXPECT_EQ(padding_packet.capture_time_ms(), kStartTime.ms() + 1); + EXPECT_EQ(padding_packet.capture_time(), kStartTime + TimeDelta::Millis(1)); } TEST_F(PacketSequencerTest, UpdatesRtxPaddingBasedOnLastMediaPacket) { @@ -242,8 +242,8 @@ TEST_F(PacketSequencerTest, UpdatesRtxPaddingBasedOnLastMediaPacket) { EXPECT_EQ( padding_packet.Timestamp(), media_packet.Timestamp() + (kTimeDelta.ms() * kTimestampTicksPerMs)); - EXPECT_EQ(padding_packet.capture_time_ms(), - media_packet.capture_time_ms() + kTimeDelta.ms()); + EXPECT_EQ(padding_packet.capture_time(), + media_packet.capture_time() + kTimeDelta); } } // namespace diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc index 723064eeba..6f90cd175c 100644 --- a/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc +++ b/modules/rtp_rtcp/source/remote_ntp_time_estimator.cc @@ -22,70 +22,83 @@ namespace webrtc { namespace { constexpr int kMinimumNumberOfSamples = 2; -constexpr int kTimingLogIntervalMs = 10000; +constexpr TimeDelta kTimingLogInterval = TimeDelta::Seconds(10); constexpr int kClocksOffsetSmoothingWindow = 100; +// Subtracts two NtpTime values keeping maximum precision. +int64_t Subtract(NtpTime minuend, NtpTime subtrahend) { + uint64_t a = static_cast(minuend); + uint64_t b = static_cast(subtrahend); + return a >= b ? static_cast(a - b) : -static_cast(b - a); +} + +NtpTime Add(NtpTime lhs, int64_t rhs) { + uint64_t result = static_cast(lhs); + if (rhs >= 0) { + result += static_cast(rhs); + } else { + result -= static_cast(-rhs); + } + return NtpTime(result); +} + } // namespace // TODO(wu): Refactor this class so that it can be shared with // vie_sync_module.cc. RemoteNtpTimeEstimator::RemoteNtpTimeEstimator(Clock* clock) : clock_(clock), - ntp_clocks_offset_estimator_(kClocksOffsetSmoothingWindow), - last_timing_log_ms_(-1) {} - -RemoteNtpTimeEstimator::~RemoteNtpTimeEstimator() {} + ntp_clocks_offset_estimator_(kClocksOffsetSmoothingWindow) {} -bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(int64_t rtt, - uint32_t ntp_secs, - uint32_t ntp_frac, +bool RemoteNtpTimeEstimator::UpdateRtcpTimestamp(TimeDelta rtt, + NtpTime sender_send_time, uint32_t rtp_timestamp) { - bool new_rtcp_sr = false; - if (!rtp_to_ntp_.UpdateMeasurements(ntp_secs, ntp_frac, rtp_timestamp, - &new_rtcp_sr)) { - return false; - } - if (!new_rtcp_sr) { - // No new RTCP SR since last time this function was called. - return true; + switch (rtp_to_ntp_.UpdateMeasurements(sender_send_time, rtp_timestamp)) { + case RtpToNtpEstimator::kInvalidMeasurement: + return false; + case RtpToNtpEstimator::kSameMeasurement: + // No new RTCP SR since last time this function was called. + return true; + case RtpToNtpEstimator::kNewMeasurement: + break; } + // Assume connection is symmetric and thus time to deliver the packet is half + // the round trip time. + int64_t deliver_time_ntp = ToNtpUnits(rtt) / 2; + // Update extrapolator with the new arrival time. - // The extrapolator assumes the ntp time. - int64_t receiver_arrival_time_ms = clock_->CurrentNtpInMilliseconds(); - int64_t sender_send_time_ms = NtpTime(ntp_secs, ntp_frac).ToMs(); - int64_t sender_arrival_time_ms = sender_send_time_ms + rtt / 2; + NtpTime receiver_arrival_time = clock_->CurrentNtpTime(); int64_t remote_to_local_clocks_offset = - receiver_arrival_time_ms - sender_arrival_time_ms; + Subtract(receiver_arrival_time, sender_send_time) - deliver_time_ntp; ntp_clocks_offset_estimator_.Insert(remote_to_local_clocks_offset); return true; } -int64_t RemoteNtpTimeEstimator::Estimate(uint32_t rtp_timestamp) { - int64_t sender_capture_ntp_ms = 0; - if (!rtp_to_ntp_.Estimate(rtp_timestamp, &sender_capture_ntp_ms)) { - return -1; +NtpTime RemoteNtpTimeEstimator::EstimateNtp(uint32_t rtp_timestamp) { + NtpTime sender_capture = rtp_to_ntp_.Estimate(rtp_timestamp); + if (!sender_capture.Valid()) { + return sender_capture; } int64_t remote_to_local_clocks_offset = ntp_clocks_offset_estimator_.GetFilteredValue(); - int64_t receiver_capture_ntp_ms = - sender_capture_ntp_ms + remote_to_local_clocks_offset; + NtpTime receiver_capture = Add(sender_capture, remote_to_local_clocks_offset); - int64_t now_ms = clock_->TimeInMilliseconds(); - if (now_ms - last_timing_log_ms_ > kTimingLogIntervalMs) { + Timestamp now = clock_->CurrentTime(); + if (now - last_timing_log_ > kTimingLogInterval) { RTC_LOG(LS_INFO) << "RTP timestamp: " << rtp_timestamp - << " in NTP clock: " << sender_capture_ntp_ms + << " in NTP clock: " << sender_capture.ToMs() << " estimated time in receiver NTP clock: " - << receiver_capture_ntp_ms; - last_timing_log_ms_ = now_ms; + << receiver_capture.ToMs(); + last_timing_log_ = now; } - return receiver_capture_ntp_ms; + return receiver_capture; } absl::optional -RemoteNtpTimeEstimator::EstimateRemoteToLocalClockOffsetMs() { +RemoteNtpTimeEstimator::EstimateRemoteToLocalClockOffset() { if (ntp_clocks_offset_estimator_.GetNumberOfSamplesStored() < kMinimumNumberOfSamples) { return absl::nullopt; diff --git a/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc b/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc index 73c3e9b9b8..8dbfaec940 100644 --- a/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc +++ b/modules/rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc @@ -9,32 +9,29 @@ */ #include "modules/rtp_rtcp/include/remote_ntp_time_estimator.h" + #include "absl/types/optional.h" +#include "modules/rtp_rtcp/source/time_util.h" #include "system_wrappers/include/clock.h" #include "system_wrappers/include/ntp_time.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { +namespace { -constexpr int64_t kTestRtt = 10; -constexpr int64_t kLocalClockInitialTimeMs = 123; -constexpr int64_t kRemoteClockInitialTimeMs = 345; +constexpr TimeDelta kTestRtt = TimeDelta::Millis(10); +constexpr Timestamp kLocalClockInitialTime = Timestamp::Millis(123); +constexpr Timestamp kRemoteClockInitialTime = Timestamp::Millis(373); constexpr uint32_t kTimestampOffset = 567; -constexpr int64_t kRemoteToLocalClockOffsetMs = - kLocalClockInitialTimeMs - kRemoteClockInitialTimeMs; +constexpr int64_t kRemoteToLocalClockOffsetNtp = + ToNtpUnits(kLocalClockInitialTime - kRemoteClockInitialTime); class RemoteNtpTimeEstimatorTest : public ::testing::Test { protected: - RemoteNtpTimeEstimatorTest() - : local_clock_(kLocalClockInitialTimeMs * 1000), - remote_clock_(kRemoteClockInitialTimeMs * 1000), - estimator_(new RemoteNtpTimeEstimator(&local_clock_)) {} - ~RemoteNtpTimeEstimatorTest() override = default; - - void AdvanceTimeMilliseconds(int64_t ms) { - local_clock_.AdvanceTimeMilliseconds(ms); - remote_clock_.AdvanceTimeMilliseconds(ms); + void AdvanceTime(TimeDelta delta) { + local_clock_.AdvanceTime(delta); + remote_clock_.AdvanceTime(delta); } uint32_t GetRemoteTimestamp() { @@ -42,107 +39,90 @@ class RemoteNtpTimeEstimatorTest : public ::testing::Test { kTimestampOffset; } - NtpTime GetRemoteNtpTime() { return remote_clock_.CurrentNtpTime(); } - void SendRtcpSr() { uint32_t rtcp_timestamp = GetRemoteTimestamp(); - NtpTime ntp = GetRemoteNtpTime(); + NtpTime ntp = remote_clock_.CurrentNtpTime(); - AdvanceTimeMilliseconds(kTestRtt / 2); - ReceiveRtcpSr(kTestRtt, rtcp_timestamp, ntp.seconds(), ntp.fractions()); + AdvanceTime(kTestRtt / 2); + RTC_DCHECK(estimator_.UpdateRtcpTimestamp(kTestRtt, ntp, rtcp_timestamp)); } - void SendRtcpSrInaccurately(int64_t ntp_error_ms, - int64_t networking_delay_ms) { + void SendRtcpSrInaccurately(TimeDelta ntp_error, TimeDelta networking_delay) { uint32_t rtcp_timestamp = GetRemoteTimestamp(); - int64_t ntp_error_fractions = - ntp_error_ms * static_cast(NtpTime::kFractionsPerSecond) / - 1000; - NtpTime ntp(static_cast(GetRemoteNtpTime()) + + int64_t ntp_error_fractions = ToNtpUnits(ntp_error); + NtpTime ntp(static_cast(remote_clock_.CurrentNtpTime()) + ntp_error_fractions); - AdvanceTimeMilliseconds(kTestRtt / 2 + networking_delay_ms); - ReceiveRtcpSr(kTestRtt, rtcp_timestamp, ntp.seconds(), ntp.fractions()); + AdvanceTime(kTestRtt / 2 + networking_delay); + RTC_DCHECK(estimator_.UpdateRtcpTimestamp(kTestRtt, ntp, rtcp_timestamp)); } - void UpdateRtcpTimestamp(int64_t rtt, - uint32_t ntp_secs, - uint32_t ntp_frac, - uint32_t rtp_timestamp, - bool expected_result) { - EXPECT_EQ(expected_result, estimator_->UpdateRtcpTimestamp( - rtt, ntp_secs, ntp_frac, rtp_timestamp)); - } - - void ReceiveRtcpSr(int64_t rtt, - uint32_t rtcp_timestamp, - uint32_t ntp_seconds, - uint32_t ntp_fractions) { - UpdateRtcpTimestamp(rtt, ntp_seconds, ntp_fractions, rtcp_timestamp, true); - } - - SimulatedClock local_clock_; - SimulatedClock remote_clock_; - std::unique_ptr estimator_; + SimulatedClock local_clock_{kLocalClockInitialTime}; + SimulatedClock remote_clock_{kRemoteClockInitialTime}; + RemoteNtpTimeEstimator estimator_{&local_clock_}; }; -TEST_F(RemoteNtpTimeEstimatorTest, Estimate) { - // Failed without valid NTP. - UpdateRtcpTimestamp(kTestRtt, 0, 0, 0, false); +TEST_F(RemoteNtpTimeEstimatorTest, FailsWithoutValidNtpTime) { + EXPECT_FALSE( + estimator_.UpdateRtcpTimestamp(kTestRtt, NtpTime(), /*rtp_timestamp=*/0)); +} - AdvanceTimeMilliseconds(1000); +TEST_F(RemoteNtpTimeEstimatorTest, Estimate) { // Remote peer sends first RTCP SR. SendRtcpSr(); // Remote sends a RTP packet. - AdvanceTimeMilliseconds(15); + AdvanceTime(TimeDelta::Millis(15)); uint32_t rtp_timestamp = GetRemoteTimestamp(); int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds(); // Local peer needs at least 2 RTCP SR to calculate the capture time. const int64_t kNotEnoughRtcpSr = -1; - EXPECT_EQ(kNotEnoughRtcpSr, estimator_->Estimate(rtp_timestamp)); - EXPECT_EQ(absl::nullopt, estimator_->EstimateRemoteToLocalClockOffsetMs()); + EXPECT_EQ(kNotEnoughRtcpSr, estimator_.Estimate(rtp_timestamp)); + EXPECT_EQ(estimator_.EstimateRemoteToLocalClockOffset(), absl::nullopt); - AdvanceTimeMilliseconds(800); + AdvanceTime(TimeDelta::Millis(800)); // Remote sends second RTCP SR. SendRtcpSr(); // Local peer gets enough RTCP SR to calculate the capture time. - EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp)); - EXPECT_EQ(kRemoteToLocalClockOffsetMs, - estimator_->EstimateRemoteToLocalClockOffsetMs()); + EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp)); + EXPECT_EQ(estimator_.EstimateRemoteToLocalClockOffset(), + kRemoteToLocalClockOffsetNtp); } TEST_F(RemoteNtpTimeEstimatorTest, AveragesErrorsOut) { // Remote peer sends first 10 RTCP SR without errors. for (int i = 0; i < 10; ++i) { - AdvanceTimeMilliseconds(1000); + AdvanceTime(TimeDelta::Seconds(1)); SendRtcpSr(); } - AdvanceTimeMilliseconds(150); + AdvanceTime(TimeDelta::Millis(150)); uint32_t rtp_timestamp = GetRemoteTimestamp(); int64_t capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds(); // Local peer gets enough RTCP SR to calculate the capture time. - EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp)); - EXPECT_EQ(kRemoteToLocalClockOffsetMs, - estimator_->EstimateRemoteToLocalClockOffsetMs()); + EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp)); + EXPECT_EQ(kRemoteToLocalClockOffsetNtp, + estimator_.EstimateRemoteToLocalClockOffset()); // Remote sends corrupted RTCP SRs - AdvanceTimeMilliseconds(1000); - SendRtcpSrInaccurately(/*ntp_error_ms=*/2, /*networking_delay_ms=*/-1); - AdvanceTimeMilliseconds(1000); - SendRtcpSrInaccurately(/*ntp_error_ms=*/-2, /*networking_delay_ms=*/1); + AdvanceTime(TimeDelta::Seconds(1)); + SendRtcpSrInaccurately(/*ntp_error=*/TimeDelta::Millis(2), + /*networking_delay=*/TimeDelta::Millis(-1)); + AdvanceTime(TimeDelta::Seconds(1)); + SendRtcpSrInaccurately(/*ntp_error=*/TimeDelta::Millis(-2), + /*networking_delay=*/TimeDelta::Millis(1)); // New RTP packet to estimate timestamp. - AdvanceTimeMilliseconds(150); + AdvanceTime(TimeDelta::Millis(150)); rtp_timestamp = GetRemoteTimestamp(); capture_ntp_time_ms = local_clock_.CurrentNtpInMilliseconds(); // Errors should be averaged out. - EXPECT_EQ(capture_ntp_time_ms, estimator_->Estimate(rtp_timestamp)); - EXPECT_EQ(kRemoteToLocalClockOffsetMs, - estimator_->EstimateRemoteToLocalClockOffsetMs()); + EXPECT_EQ(capture_ntp_time_ms, estimator_.Estimate(rtp_timestamp)); + EXPECT_EQ(kRemoteToLocalClockOffsetNtp, + estimator_.EstimateRemoteToLocalClockOffset()); } +} // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_packet/dlrr.h b/modules/rtp_rtcp/source/rtcp_packet/dlrr.h index 6fe2099fd9..ad91dfdcc6 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/dlrr.h +++ b/modules/rtp_rtcp/source/rtcp_packet/dlrr.h @@ -24,11 +24,21 @@ struct ReceiveTimeInfo { ReceiveTimeInfo() : ssrc(0), last_rr(0), delay_since_last_rr(0) {} ReceiveTimeInfo(uint32_t ssrc, uint32_t last_rr, uint32_t delay) : ssrc(ssrc), last_rr(last_rr), delay_since_last_rr(delay) {} + uint32_t ssrc; uint32_t last_rr; uint32_t delay_since_last_rr; }; +inline bool operator==(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) { + return lhs.ssrc == rhs.ssrc && lhs.last_rr == rhs.last_rr && + lhs.delay_since_last_rr == rhs.delay_since_last_rr; +} + +inline bool operator!=(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) { + return !(lhs == rhs); +} + // DLRR Report Block: Delay since the Last Receiver Report (RFC 3611). class Dlrr { public: diff --git a/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc index 7c50c01c43..3d9a2a3408 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc @@ -24,19 +24,6 @@ using webrtc::rtcp::ReceiveTimeInfo; using webrtc::rtcp::Rrtr; namespace webrtc { -// Define comparision operators that shouldn't be needed in production, -// but make testing matches more clear. -namespace rtcp { -bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) { - return rrtr1.ntp() == rrtr2.ntp(); -} - -bool operator==(const ReceiveTimeInfo& time1, const ReceiveTimeInfo& time2) { - return time1.ssrc == time2.ssrc && time1.last_rr == time2.last_rr && - time1.delay_since_last_rr == time2.delay_since_last_rr; -} -} // namespace rtcp - namespace { constexpr uint32_t kSenderSsrc = 0x12345678; constexpr uint8_t kEmptyPacket[] = {0x80, 207, 0x00, 0x01, diff --git a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h index b23008c528..0f70cf75c3 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h +++ b/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h @@ -42,8 +42,8 @@ class LossNotification : public Psfb { // Set all of the values transmitted by the loss notification message. // If the values may not be represented by a loss notification message, // false is returned, and no change is made to the object; this happens - // 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, + // when `last_received` is ahead of `last_decoded` by more than 0x7fff. + // This is because `last_received` 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, diff --git a/modules/rtp_rtcp/source/rtcp_packet/rrtr.h b/modules/rtp_rtcp/source/rtcp_packet/rrtr.h index 8eb4ce62ad..827bd74399 100644 --- a/modules/rtp_rtcp/source/rtcp_packet/rrtr.h +++ b/modules/rtp_rtcp/source/rtcp_packet/rrtr.h @@ -46,6 +46,14 @@ class Rrtr { NtpTime ntp_; }; +inline bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) { + return rrtr1.ntp() == rrtr2.ntp(); +} + +inline bool operator!=(const Rrtr& rrtr1, const Rrtr& rrtr2) { + return !(rrtr1 == rrtr2); +} + } // namespace rtcp } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_ diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc index d0f9596837..6eb8dee2d1 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver.cc @@ -669,7 +669,6 @@ void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, (clock_->CurrentNtpInMilliseconds() - rtc::kNtpJan1970Millisecs) * rtc::kNumMicrosecsPerMillisec); - int64_t rtt_ms = 0; uint32_t send_time_ntp = report_block.last_sr(); // RFC3550, section 6.4.1, LSR field discription states: // If no SR has been received yet, the field is set to zero. @@ -691,13 +690,13 @@ void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, // RTT in 1/(2^16) seconds. uint32_t rtt_ntp = receive_time_ntp - delay_ntp - send_time_ntp; // Convert to 1/1000 seconds (milliseconds). - rtt_ms = CompactNtpRttToMs(rtt_ntp); - report_block_data->AddRoundTripTimeSample(rtt_ms); + TimeDelta rtt = CompactNtpRttToTimeDelta(rtt_ntp); + report_block_data->AddRoundTripTimeSample(rtt.ms()); if (report_block.source_ssrc() == main_ssrc_) { - rtts_[remote_ssrc].AddRtt(TimeDelta::Millis(rtt_ms)); + rtts_[remote_ssrc].AddRtt(rtt); } - packet_information->rtt_ms = rtt_ms; + packet_information->rtt_ms = rtt.ms(); } packet_information->report_blocks.push_back( @@ -941,9 +940,10 @@ void RTCPReceiver::HandleXrDlrrReportBlock(uint32_t sender_ssrc, uint32_t now_ntp = CompactNtp(clock_->CurrentNtpTime()); uint32_t rtt_ntp = now_ntp - delay_ntp - send_time_ntp; - xr_rr_rtt_ms_ = CompactNtpRttToMs(rtt_ntp); + TimeDelta rtt = CompactNtpRttToTimeDelta(rtt_ntp); + xr_rr_rtt_ms_ = rtt.ms(); - non_sender_rtts_[sender_ssrc].Update(TimeDelta::Millis(xr_rr_rtt_ms_)); + non_sender_rtts_[sender_ssrc].Update(rtt); } void RTCPReceiver::HandleXrTargetBitrate( diff --git a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index eb5d265c6a..95dc9df9ae 100644 --- a/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -15,6 +15,7 @@ #include #include "api/array_view.h" +#include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "api/video/video_bitrate_allocation.h" #include "api/video/video_bitrate_allocator.h" @@ -244,15 +245,15 @@ TEST(RtcpReceiverTest, InjectSrPacketCalculatesRTT) { RTCPReceiver receiver(DefaultConfiguration(&mocks), &mocks.rtp_rtcp_impl); receiver.SetRemoteSSRC(kSenderSsrc); - const int64_t kRttMs = 123; + const TimeDelta kRtt = TimeDelta::Millis(123); const uint32_t kDelayNtp = 0x4321; - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); int64_t rtt_ms = 0; EXPECT_EQ(-1, receiver.RTT(kSenderSsrc, &rtt_ms, nullptr, nullptr, nullptr)); uint32_t sent_ntp = CompactNtp(mocks.clock.CurrentNtpTime()); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::SenderReport sr; sr.SetSenderSsrc(kSenderSsrc); @@ -267,7 +268,7 @@ TEST(RtcpReceiverTest, InjectSrPacketCalculatesRTT) { receiver.IncomingPacket(sr.Build()); EXPECT_EQ(0, receiver.RTT(kSenderSsrc, &rtt_ms, nullptr, nullptr, nullptr)); - EXPECT_NEAR(kRttMs, rtt_ms, 1); + EXPECT_NEAR(rtt_ms, kRtt.ms(), 1); } TEST(RtcpReceiverTest, InjectSrPacketCalculatesNegativeRTTAsOne) { @@ -275,15 +276,15 @@ TEST(RtcpReceiverTest, InjectSrPacketCalculatesNegativeRTTAsOne) { RTCPReceiver receiver(DefaultConfiguration(&mocks), &mocks.rtp_rtcp_impl); receiver.SetRemoteSSRC(kSenderSsrc); - const int64_t kRttMs = -13; + const TimeDelta kRtt = TimeDelta::Millis(-13); const uint32_t kDelayNtp = 0x4321; - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); int64_t rtt_ms = 0; EXPECT_EQ(-1, receiver.RTT(kSenderSsrc, &rtt_ms, nullptr, nullptr, nullptr)); uint32_t sent_ntp = CompactNtp(mocks.clock.CurrentNtpTime()); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::SenderReport sr; sr.SetSenderSsrc(kSenderSsrc); @@ -308,12 +309,12 @@ TEST(RtcpReceiverTest, RTCPReceiver receiver(DefaultConfiguration(&mocks), &mocks.rtp_rtcp_impl); receiver.SetRemoteSSRC(kSenderSsrc); - const int64_t kRttMs = 120; + const TimeDelta kRtt = TimeDelta::Millis(120); const uint32_t kDelayNtp = 123000; - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); uint32_t sent_ntp = CompactNtp(mocks.clock.CurrentNtpTime()); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::SenderReport sr; sr.SetSenderSsrc(kSenderSsrc); @@ -328,7 +329,7 @@ TEST(RtcpReceiverTest, EXPECT_CALL(mocks.rtp_rtcp_impl, OnReceivedRtcpReportBlocks(SizeIs(2))); EXPECT_CALL(mocks.bandwidth_observer, - OnReceivedRtcpReceiverReport(SizeIs(2), kRttMs, _)); + OnReceivedRtcpReceiverReport(SizeIs(2), kRtt.ms(), _)); receiver.IncomingPacket(sr.Build()); } @@ -830,7 +831,7 @@ TEST(RtcpReceiverTest, InjectExtendedReportsDlrrPacketWithSubBlock) { uint32_t compact_ntp_now = CompactNtp(mocks.clock.CurrentNtpTime()); EXPECT_TRUE(receiver.GetAndResetXrRrRtt(&rtt_ms)); uint32_t rtt_ntp = compact_ntp_now - kDelay - kLastRR; - EXPECT_NEAR(CompactNtpRttToMs(rtt_ntp), rtt_ms, 1); + EXPECT_NEAR(CompactNtpRttToTimeDelta(rtt_ntp).ms(), rtt_ms, 1); RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_GT(non_sender_rtt_stats.round_trip_time(), TimeDelta::Zero()); @@ -860,7 +861,7 @@ TEST(RtcpReceiverTest, InjectExtendedReportsDlrrPacketWithMultipleSubBlocks) { int64_t rtt_ms = 0; EXPECT_TRUE(receiver.GetAndResetXrRrRtt(&rtt_ms)); uint32_t rtt_ntp = compact_ntp_now - kDelay - kLastRR; - EXPECT_NEAR(CompactNtpRttToMs(rtt_ntp), rtt_ms, 1); + EXPECT_NEAR(CompactNtpRttToTimeDelta(rtt_ntp).ms(), rtt_ms, 1); RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_GT(non_sender_rtt_stats.round_trip_time(), TimeDelta::Zero()); @@ -947,12 +948,12 @@ TEST(RtcpReceiverTest, RttCalculatedAfterExtendedReportsDlrr) { receiver.SetRemoteSSRC(kSenderSsrc); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -962,7 +963,7 @@ TEST(RtcpReceiverTest, RttCalculatedAfterExtendedReportsDlrr) { int64_t rtt_ms = 0; EXPECT_TRUE(receiver.GetAndResetXrRrRtt(&rtt_ms)); - EXPECT_NEAR(kRttMs, rtt_ms, 1); + EXPECT_NEAR(kRtt.ms(), rtt_ms, 1); RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); @@ -982,12 +983,12 @@ TEST(RtcpReceiverTest, SetterEnablesReceiverRtt) { receiver.SetNonSenderRttMeasurement(true); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -997,7 +998,7 @@ TEST(RtcpReceiverTest, SetterEnablesReceiverRtt) { int64_t rtt_ms = 0; EXPECT_TRUE(receiver.GetAndResetXrRrRtt(&rtt_ms)); - EXPECT_NEAR(rtt_ms, kRttMs, 1); + EXPECT_NEAR(rtt_ms, kRtt.ms(), 1); RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); @@ -1017,12 +1018,12 @@ TEST(RtcpReceiverTest, DoesntCalculateRttOnReceivedDlrr) { receiver.SetNonSenderRttMeasurement(false); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1048,12 +1049,12 @@ TEST(RtcpReceiverTest, XrDlrrCalculatesNegativeRttAsOne) { receiver.SetRemoteSSRC(kSenderSsrc); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(-3600 * 1000, -1); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(-3600 * 1000, -1)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1081,12 +1082,12 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleMeasurements) { receiver.SetRemoteSSRC(kSenderSsrc); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1099,7 +1100,7 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleMeasurements) { RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRttMs, 1); + EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRtt.ms(), 1); EXPECT_EQ(non_sender_rtt_stats.round_trip_time_measurements(), 1); EXPECT_EQ(non_sender_rtt_stats.total_round_trip_time().ms(), non_sender_rtt_stats.round_trip_time()->ms()); @@ -1107,7 +1108,7 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleMeasurements) { // Generate another XR report with the same RTT and delay. NtpTime now2 = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp2 = CompactNtp(now2); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr2; xr2.SetSenderSsrc(kSenderSsrc); @@ -1119,9 +1120,10 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleMeasurements) { // the values are as expected. non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRttMs, 1); + EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRtt.ms(), 1); EXPECT_EQ(non_sender_rtt_stats.round_trip_time_measurements(), 2); - EXPECT_NEAR(non_sender_rtt_stats.total_round_trip_time().ms(), 2 * kRttMs, 2); + EXPECT_NEAR(non_sender_rtt_stats.total_round_trip_time().ms(), 2 * kRtt.ms(), + 2); } // Test that the receiver RTT stat resets when receiving a SR without XR. This @@ -1135,12 +1137,12 @@ TEST(RtcpReceiverTest, ReceiverRttResetOnSrWithoutXr) { receiver.SetRemoteSSRC(kSenderSsrc); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1151,7 +1153,7 @@ TEST(RtcpReceiverTest, ReceiverRttResetOnSrWithoutXr) { RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRttMs, 1); + EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRtt.ms(), 1); // Generate a SR without XR. rtcp::ReportBlock rb; @@ -1180,12 +1182,12 @@ TEST(RtcpReceiverTest, ReceiverRttResetOnDlrrWithZeroTimestamp) { receiver.SetRemoteSSRC(kSenderSsrc); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1196,12 +1198,12 @@ TEST(RtcpReceiverTest, ReceiverRttResetOnDlrrWithZeroTimestamp) { RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRttMs, 1); + EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRtt.ms(), 1); // Generate an XR+DLRR with zero timestamp. rtcp::ExtendedReports xr2; xr2.SetSenderSsrc(kSenderSsrc); - xr2.AddDlrrItem(ReceiveTimeInfo(kReceiverMainSsrc, 0, kDelayMs)); + xr2.AddDlrrItem(ReceiveTimeInfo(kReceiverMainSsrc, 0, kDelayNtp)); receiver.IncomingPacket(xr2.Build()); @@ -1220,12 +1222,12 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleRemoteSsrcs) { receiver.SetNonSenderRttMeasurement(true); Random rand(0x0123456789abcdef); - const int64_t kRttMs = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); NtpTime now = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp = CompactNtp(now); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::ExtendedReports xr; xr.SetSenderSsrc(kSenderSsrc); @@ -1234,12 +1236,12 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleRemoteSsrcs) { receiver.IncomingPacket(xr.Build()); // Generate an XR report for another SSRC. - const int64_t kRttMs2 = rand.Rand(1, 9 * 3600 * 1000); + const TimeDelta kRtt2 = TimeDelta::Millis(rand.Rand(1, 9 * 3600 * 1000)); const uint32_t kDelayNtp2 = rand.Rand(0, 0x7fffffff); - const int64_t kDelayMs2 = CompactNtpRttToMs(kDelayNtp2); + const TimeDelta kDelay2 = CompactNtpRttToTimeDelta(kDelayNtp2); NtpTime now2 = mocks.clock.CurrentNtpTime(); uint32_t sent_ntp2 = CompactNtp(now2); - mocks.clock.AdvanceTimeMilliseconds(kRttMs2 + kDelayMs2); + mocks.clock.AdvanceTime(kRtt2 + kDelay2); rtcp::ExtendedReports xr2; xr2.SetSenderSsrc(kSenderSsrc + 1); @@ -1251,7 +1253,7 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleRemoteSsrcs) { RTCPReceiver::NonSenderRttStats non_sender_rtt_stats = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRttMs, 1); + EXPECT_NEAR(non_sender_rtt_stats.round_trip_time()->ms(), kRtt.ms(), 1); EXPECT_FALSE(non_sender_rtt_stats.total_round_trip_time().IsZero()); EXPECT_GT(non_sender_rtt_stats.round_trip_time_measurements(), 0); @@ -1260,7 +1262,7 @@ TEST(RtcpReceiverTest, ReceiverRttWithMultipleRemoteSsrcs) { RTCPReceiver::NonSenderRttStats non_sender_rtt_stats2 = receiver.GetNonSenderRTT(); EXPECT_TRUE(non_sender_rtt_stats2.round_trip_time().has_value()); - EXPECT_NEAR(non_sender_rtt_stats2.round_trip_time()->ms(), kRttMs2, 1); + EXPECT_NEAR(non_sender_rtt_stats2.round_trip_time()->ms(), kRtt2.ms(), 1); EXPECT_FALSE(non_sender_rtt_stats2.total_round_trip_time().IsZero()); EXPECT_GT(non_sender_rtt_stats2.round_trip_time_measurements(), 0); } @@ -1615,12 +1617,12 @@ TEST(RtcpReceiverTest, VerifyRttObtainedFromReportBlockDataObserver) { RTCPReceiver receiver(config, &mocks.rtp_rtcp_impl); receiver.SetRemoteSSRC(kSenderSsrc); - const int64_t kRttMs = 120; + const TimeDelta kRtt = TimeDelta::Millis(120); const uint32_t kDelayNtp = 123000; - const int64_t kDelayMs = CompactNtpRttToMs(kDelayNtp); + const TimeDelta kDelay = CompactNtpRttToTimeDelta(kDelayNtp); uint32_t sent_ntp = CompactNtp(mocks.clock.CurrentNtpTime()); - mocks.clock.AdvanceTimeMilliseconds(kRttMs + kDelayMs); + mocks.clock.AdvanceTime(kRtt + kDelay); rtcp::SenderReport sr; sr.SetSenderSsrc(kSenderSsrc); @@ -1641,10 +1643,10 @@ TEST(RtcpReceiverTest, VerifyRttObtainedFromReportBlockDataObserver) { EXPECT_EQ(kReceiverMainSsrc, report_block_data.report_block().source_ssrc); EXPECT_EQ(1u, report_block_data.num_rtts()); - EXPECT_EQ(kRttMs, report_block_data.min_rtt_ms()); - EXPECT_EQ(kRttMs, report_block_data.max_rtt_ms()); - EXPECT_EQ(kRttMs, report_block_data.sum_rtt_ms()); - EXPECT_EQ(kRttMs, report_block_data.last_rtt_ms()); + EXPECT_EQ(kRtt.ms(), report_block_data.min_rtt_ms()); + EXPECT_EQ(kRtt.ms(), report_block_data.max_rtt_ms()); + EXPECT_EQ(kRtt.ms(), report_block_data.sum_rtt_ms()); + EXPECT_EQ(kRtt.ms(), report_block_data.last_rtt_ms()); }); EXPECT_CALL(observer, OnReportBlockDataUpdated) .WillOnce([](ReportBlockData report_block_data) { diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc index a07e5aa641..04f8041bd4 100644 --- a/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/modules/rtp_rtcp/source/rtcp_sender.cc @@ -484,7 +484,9 @@ void RTCPSender::BuildRR(const RtcpContext& ctx, PacketSender& sender) { rtcp::ReceiverReport report; report.SetSenderSsrc(ssrc_); report.SetReportBlocks(CreateReportBlocks(ctx.feedback_state_)); - sender.AppendPacket(report); + if (method_ == RtcpMode::kCompound || !report.report_blocks().empty()) { + sender.AppendPacket(report); + } } void RTCPSender::BuildPLI(const RtcpContext& ctx, PacketSender& sender) { diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc index d05d8d6dd5..38a9302b67 100644 --- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc @@ -252,13 +252,20 @@ TEST_F(RtcpSenderTest, DoNotSendCompundBeforeRtp) { TEST_F(RtcpSenderTest, SendRr) { auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); EXPECT_EQ(0, rtcp_sender->SendRTCP(feedback_state(), kRtcpRr)); EXPECT_EQ(1, parser()->receiver_report()->num_packets()); EXPECT_EQ(kSenderSsrc, parser()->receiver_report()->sender_ssrc()); EXPECT_EQ(0U, parser()->receiver_report()->report_blocks().size()); } +TEST_F(RtcpSenderTest, DoesntSendEmptyRrInReducedSizeMode) { + auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); + rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); + EXPECT_EQ(parser()->receiver_report()->num_packets(), 0); +} + TEST_F(RtcpSenderTest, SendRrWithOneReportBlock) { const uint16_t kSeqNum = 11111; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); @@ -415,7 +422,7 @@ TEST_F(RtcpSenderTest, SendLossNotificationBufferingAllowed) { TEST_F(RtcpSenderTest, RembNotIncludedBeforeSet) { auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); @@ -427,7 +434,7 @@ TEST_F(RtcpSenderTest, RembNotIncludedAfterUnset) { const int64_t kBitrate = 261011; const std::vector kSsrcs = {kRemoteSsrc, kRemoteSsrc + 1}; auto rtcp_sender = CreateRtcpSender(GetDefaultConfig()); - rtcp_sender->SetRTCPStatus(RtcpMode::kReducedSize); + rtcp_sender->SetRTCPStatus(RtcpMode::kCompound); rtcp_sender->SetRemb(kBitrate, kSsrcs); rtcp_sender->SendRTCP(feedback_state(), kRtcpRr); ASSERT_EQ(1, parser()->receiver_report()->num_packets()); diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc index 02c0fef9f9..dea6286096 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc @@ -48,13 +48,13 @@ bool RtcpTransceiverConfig::Validate() const { RTC_LOG(LS_ERROR) << debug_id << "outgoing transport must be set"; return false; } - if (initial_report_delay_ms < 0) { - RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay_ms + if (initial_report_delay < TimeDelta::Zero()) { + RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay.ms() << "ms before first report shouldn't be negative."; return false; } - if (report_period_ms <= 0) { - RTC_LOG(LS_ERROR) << debug_id << "period " << report_period_ms + if (report_period <= TimeDelta::Zero()) { + RTC_LOG(LS_ERROR) << debug_id << "period " << report_period.ms() << "ms between reports should be positive."; return false; } diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h index 0be633fc61..3122ad5c36 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_config.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -97,6 +97,13 @@ class RtpStreamRtcpHandler { int last_clock_rate_ = 90'000; }; virtual RtpStats SentStats() = 0; + + virtual void OnNack(uint32_t sender_ssrc, + rtc::ArrayView sequence_numbers) {} + virtual void OnFir(uint32_t sender_ssrc) {} + virtual void OnPli(uint32_t sender_ssrc) {} + virtual void OnReportBlock(uint32_t sender_ssrc, + const rtcp::ReportBlock& report_block) {} }; struct RtcpTransceiverConfig { @@ -149,10 +156,10 @@ struct RtcpTransceiverConfig { // Initial state if `outgoing_transport` ready to accept packets. bool initial_ready_to_send = true; // Delay before 1st periodic compound packet. - int initial_report_delay_ms = 500; + TimeDelta initial_report_delay = TimeDelta::Millis(500); // Period between periodic compound packets. - int report_period_ms = 1000; + TimeDelta report_period = TimeDelta::Seconds(1); // // Flags for features and experiments. @@ -162,6 +169,15 @@ struct RtcpTransceiverConfig { // https://tools.ietf.org/html/rfc3611#section-4.4 and #section-4.5 bool non_sender_rtt_measurement = false; + // Reply to incoming RRTR messages so that remote endpoint may estimate RTT as + // non-sender as described in https://tools.ietf.org/html/rfc3611#section-4.4 + // and #section-4.5 + bool reply_to_non_sender_rtt_measurement = true; + + // Reply to incoming RRTR messages multiple times, one per sender SSRC, to + // support clients that calculate and process RTT per sender SSRC. + bool reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = true; + // Allows a REMB message to be sent immediately when SetRemb is called without // having to wait for the next compount message to be sent. bool send_remb_on_change = false; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc index 94451efcd7..9ec1749860 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -32,6 +32,7 @@ #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" #include "modules/rtp_rtcp/source/time_util.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_map.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/divide_round.h" #include "rtc_base/task_utils/repeating_task.h" @@ -56,6 +57,9 @@ struct RtcpTransceiverImpl::RemoteSenderState { struct RtcpTransceiverImpl::LocalSenderState { uint32_t ssrc; + size_t last_num_sent_bytes = 0; + // Sequence number of the last FIR message per sender SSRC. + flat_map last_fir; RtpStreamRtcpHandler* handler = nullptr; }; @@ -99,7 +103,7 @@ RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config) : config_(config), ready_to_send_(config.initial_ready_to_send) { RTC_CHECK(config_.Validate()); if (ready_to_send_ && config_.schedule_periodic_compound_packets) { - SchedulePeriodicCompoundPackets(config_.initial_report_delay_ms); + SchedulePeriodicCompoundPackets(config_.initial_report_delay); } } @@ -158,7 +162,7 @@ void RtcpTransceiverImpl::SetReadyToSend(bool ready) { periodic_task_handle_.Stop(); if (!ready_to_send_ && ready) // Restart periodic sending. - SchedulePeriodicCompoundPackets(config_.report_period_ms / 2); + SchedulePeriodicCompoundPackets(config_.report_period / 2); } ready_to_send_ = ready; } @@ -312,6 +316,7 @@ void RtcpTransceiverImpl::HandleSenderReport( remote_senders_[sender_report.sender_ssrc()]; remote_sender.last_received_sender_report = {{now, sender_report.ntp()}}; const auto& received_report_blocks = sender_report.report_blocks(); + CallbackOnReportBlocks(sender_report.sender_ssrc(), received_report_blocks); report_blocks.insert(report_blocks.end(), received_report_blocks.begin(), received_report_blocks.end()); @@ -328,32 +333,116 @@ void RtcpTransceiverImpl::HandleReceiverReport( return; } const auto& received_report_blocks = receiver_report.report_blocks(); + CallbackOnReportBlocks(receiver_report.sender_ssrc(), received_report_blocks); report_blocks.insert(report_blocks.end(), received_report_blocks.begin(), received_report_blocks.end()); } +void RtcpTransceiverImpl::CallbackOnReportBlocks( + uint32_t sender_ssrc, + rtc::ArrayView report_blocks) { + if (local_senders_.empty()) { + return; + } + for (const rtcp::ReportBlock& block : report_blocks) { + auto sender_it = local_senders_by_ssrc_.find(block.source_ssrc()); + if (sender_it != local_senders_by_ssrc_.end()) { + sender_it->second->handler->OnReportBlock(sender_ssrc, block); + } + } +} + void RtcpTransceiverImpl::HandlePayloadSpecificFeedback( const rtcp::CommonHeader& rtcp_packet_header, Timestamp now) { - // Remb is the only payload specific message handled right now. - if (rtcp_packet_header.fmt() != rtcp::Psfb::kAfbMessageType || - config_.network_link_observer == nullptr) { + switch (rtcp_packet_header.fmt()) { + case rtcp::Fir::kFeedbackMessageType: + HandleFir(rtcp_packet_header); + break; + case rtcp::Pli::kFeedbackMessageType: + HandlePli(rtcp_packet_header); + break; + case rtcp::Psfb::kAfbMessageType: + HandleRemb(rtcp_packet_header, now); + break; + } +} + +void RtcpTransceiverImpl::HandleFir( + const rtcp::CommonHeader& rtcp_packet_header) { + rtcp::Fir fir; + if (local_senders_.empty() || !fir.Parse(rtcp_packet_header)) { return; } + for (const rtcp::Fir::Request& r : fir.requests()) { + auto it = local_senders_by_ssrc_.find(r.ssrc); + if (it == local_senders_by_ssrc_.end()) { + continue; + } + auto [fir_it, is_new] = + it->second->last_fir.emplace(fir.sender_ssrc(), r.seq_nr); + if (is_new || fir_it->second != r.seq_nr) { + it->second->handler->OnFir(fir.sender_ssrc()); + fir_it->second = r.seq_nr; + } + } +} + +void RtcpTransceiverImpl::HandlePli( + const rtcp::CommonHeader& rtcp_packet_header) { + rtcp::Pli pli; + if (local_senders_.empty() || !pli.Parse(rtcp_packet_header)) { + return; + } + auto it = local_senders_by_ssrc_.find(pli.media_ssrc()); + if (it != local_senders_by_ssrc_.end()) { + it->second->handler->OnPli(pli.sender_ssrc()); + } +} + +void RtcpTransceiverImpl::HandleRemb( + const rtcp::CommonHeader& rtcp_packet_header, + Timestamp now) { rtcp::Remb remb; - if (remb.Parse(rtcp_packet_header)) { - config_.network_link_observer->OnReceiverEstimatedMaxBitrate( - now, DataRate::BitsPerSec(remb.bitrate_bps())); + if (config_.network_link_observer == nullptr || + !remb.Parse(rtcp_packet_header)) { + return; } + config_.network_link_observer->OnReceiverEstimatedMaxBitrate( + now, DataRate::BitsPerSec(remb.bitrate_bps())); } void RtcpTransceiverImpl::HandleRtpFeedback( const rtcp::CommonHeader& rtcp_packet_header, Timestamp now) { - // Transport feedback is the only message handled right now. - if (rtcp_packet_header.fmt() != - rtcp::TransportFeedback::kFeedbackMessageType || - config_.network_link_observer == nullptr) { + switch (rtcp_packet_header.fmt()) { + case rtcp::Nack::kFeedbackMessageType: + HandleNack(rtcp_packet_header); + break; + case rtcp::TransportFeedback::kFeedbackMessageType: + HandleTransportFeedback(rtcp_packet_header, now); + break; + } +} + +void RtcpTransceiverImpl::HandleNack( + const rtcp::CommonHeader& rtcp_packet_header) { + rtcp::Nack nack; + if (local_senders_.empty() || !nack.Parse(rtcp_packet_header)) { + return; + } + auto it = local_senders_by_ssrc_.find(nack.media_ssrc()); + if (it != local_senders_by_ssrc_.end()) { + it->second->handler->OnNack(nack.sender_ssrc(), nack.packet_ids()); + } +} + +void RtcpTransceiverImpl::HandleTransportFeedback( + const rtcp::CommonHeader& rtcp_packet_header, + Timestamp now) { + RTC_DCHECK_EQ(rtcp_packet_header.fmt(), + rtcp::TransportFeedback::kFeedbackMessageType); + if (config_.network_link_observer == nullptr) { return; } rtcp::TransportFeedback feedback; @@ -369,6 +458,14 @@ void RtcpTransceiverImpl::HandleExtendedReports( if (!extended_reports.Parse(rtcp_packet_header)) return; + if (config_.reply_to_non_sender_rtt_measurement && extended_reports.rrtr()) { + RrtrTimes& rrtr = received_rrtrs_[extended_reports.sender_ssrc()]; + rrtr.received_remote_mid_ntp_time = + CompactNtp(extended_reports.rrtr()->ntp()); + rrtr.local_receive_mid_ntp_time = + CompactNtp(config_.clock->ConvertTimestampToNtpTime(now)); + } + if (extended_reports.dlrr()) HandleDlrr(extended_reports.dlrr(), now); @@ -391,8 +488,8 @@ void RtcpTransceiverImpl::HandleDlrr(const rtcp::Dlrr& dlrr, Timestamp now) { if (rti.ssrc != config_.feedback_ssrc) continue; uint32_t rtt_ntp = receive_time_ntp - rti.delay_since_last_rr - rti.last_rr; - int64_t rtt_ms = CompactNtpRttToMs(rtt_ntp); - config_.network_link_observer->OnRttUpdate(now, TimeDelta::Millis(rtt_ms)); + TimeDelta rtt = CompactNtpRttToTimeDelta(rtt_ntp); + config_.network_link_observer->OnRttUpdate(now, rtt); } } @@ -417,7 +514,7 @@ void RtcpTransceiverImpl::ProcessReportBlocks( uint32_t rtt_ntp = receive_time_ntp - report_block.delay_since_last_sr() - report_block.last_sr(); - rtt_sum += TimeDelta::Millis(CompactNtpRttToMs(rtt_ntp)); + rtt_sum += CompactNtpRttToTimeDelta(rtt_ntp); ++num_rtts; } // For backward compatibility, do not report rtt based on report blocks to the @@ -461,51 +558,48 @@ void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets() { return; periodic_task_handle_.Stop(); RTC_DCHECK(ready_to_send_); - SchedulePeriodicCompoundPackets(config_.report_period_ms); + SchedulePeriodicCompoundPackets(config_.report_period); } -void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { - periodic_task_handle_ = RepeatingTaskHandle::DelayedStart( - config_.task_queue, TimeDelta::Millis(delay_ms), [this] { +void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(TimeDelta delay) { + periodic_task_handle_ = + RepeatingTaskHandle::DelayedStart(config_.task_queue, delay, [this] { RTC_DCHECK(config_.schedule_periodic_compound_packets); RTC_DCHECK(ready_to_send_); SendPeriodicCompoundPacket(); - return TimeDelta::Millis(config_.report_period_ms); + return config_.report_period; }); } -RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( +std::vector RtcpTransceiverImpl::FillReports( Timestamp now, - size_t reserved_bytes, + ReservedBytes reserved, PacketSender& rtcp_sender) { // Sender/receiver reports should be first in the RTCP packet. RTC_DCHECK(rtcp_sender.IsEmpty()); size_t available_bytes = config_.max_packet_size; - if (reserved_bytes > available_bytes) { - // Because reserved_bytes is unsigned, substracting would underflow and will - // not produce desired result. + if (reserved.per_packet > available_bytes) { + // Because reserved.per_packet is unsigned, substracting would underflow and + // will not produce desired result. available_bytes = 0; } else { - available_bytes -= reserved_bytes; + available_bytes -= reserved.per_packet; } - CompoundPacketInfo result; - result.sender_ssrc = config_.feedback_ssrc; - result.has_sender_report = false; - - static constexpr size_t kSenderReportSizeBytes = 28; - static constexpr size_t kFullSenderReportSizeBytes = - kSenderReportSizeBytes + + const size_t sender_report_size_bytes = 28 + reserved.per_sender; + const size_t full_sender_report_size_bytes = + sender_report_size_bytes + rtcp::SenderReport::kMaxNumberOfReportBlocks * rtcp::ReportBlock::kLength; - size_t max_full_sender_reports = available_bytes / kFullSenderReportSizeBytes; + size_t max_full_sender_reports = + available_bytes / full_sender_report_size_bytes; size_t max_report_blocks = max_full_sender_reports * rtcp::SenderReport::kMaxNumberOfReportBlocks; size_t available_bytes_for_last_sender_report = - available_bytes - max_full_sender_reports * kFullSenderReportSizeBytes; - if (available_bytes_for_last_sender_report >= kSenderReportSizeBytes) { + available_bytes - max_full_sender_reports * full_sender_report_size_bytes; + if (available_bytes_for_last_sender_report >= sender_report_size_bytes) { max_report_blocks += - (available_bytes_for_last_sender_report - kSenderReportSizeBytes) / + (available_bytes_for_last_sender_report - sender_report_size_bytes) / rtcp::ReportBlock::kLength; } @@ -516,17 +610,28 @@ RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( // is low, more sender reports may fit in. size_t max_sender_reports = (available_bytes - report_blocks.size() * rtcp::ReportBlock::kLength) / - kSenderReportSizeBytes; + sender_report_size_bytes; auto last_handled_sender_it = local_senders_.end(); auto report_block_it = report_blocks.begin(); - size_t num_sender_reports = 0; + std::vector sender_ssrcs; for (auto it = local_senders_.begin(); - it != local_senders_.end() && num_sender_reports < max_sender_reports; + it != local_senders_.end() && sender_ssrcs.size() < max_sender_reports; ++it) { LocalSenderState& rtp_sender = *it; RtpStreamRtcpHandler::RtpStats stats = rtp_sender.handler->SentStats(); + if (stats.num_sent_bytes() < rtp_sender.last_num_sent_bytes) { + RTC_LOG(LS_ERROR) << "Inconsistent SR for SSRC " << rtp_sender.ssrc + << ". Number of total sent bytes decreased."; + rtp_sender.last_num_sent_bytes = 0; + } + if (stats.num_sent_bytes() == rtp_sender.last_num_sent_bytes) { + // Skip because no RTP packet was send for this SSRC since last report. + continue; + } + rtp_sender.last_num_sent_bytes = stats.num_sent_bytes(); + last_handled_sender_it = it; rtcp::SenderReport sender_report; sender_report.SetSenderSsrc(rtp_sender.ssrc); @@ -548,12 +653,7 @@ RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( report_block_it += num_blocks; } rtcp_sender.AppendPacket(sender_report); - ++num_sender_reports; - - if (!result.has_sender_report) { - result.has_sender_report = true; - result.sender_ssrc = rtp_sender.ssrc; - } + sender_ssrcs.push_back(rtp_sender.ssrc); } if (last_handled_sender_it != local_senders_.end()) { // Rotate `local_senders_` so that the 1st unhandled sender become first in @@ -571,14 +671,16 @@ RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( // In compound mode each RTCP packet has to start with a sender or receiver // report. - if (config_.rtcp_mode == RtcpMode::kCompound && num_sender_reports == 0 && + if (config_.rtcp_mode == RtcpMode::kCompound && sender_ssrcs.empty() && num_receiver_reports == 0) { num_receiver_reports = 1; } + uint32_t sender_ssrc = + sender_ssrcs.empty() ? config_.feedback_ssrc : sender_ssrcs.front(); for (size_t i = 0; i < num_receiver_reports; ++i) { rtcp::ReceiverReport receiver_report; - receiver_report.SetSenderSsrc(result.sender_ssrc); + receiver_report.SetSenderSsrc(sender_ssrc); size_t num_blocks = std::min(rtcp::ReceiverReport::kMaxNumberOfReportBlocks, report_blocks.end() - report_block_it); @@ -590,47 +692,87 @@ RtcpTransceiverImpl::CompoundPacketInfo RtcpTransceiverImpl::FillReports( } // All report blocks should be attached at this point. RTC_DCHECK_EQ(report_blocks.end() - report_block_it, 0); - return result; + return sender_ssrcs; } void RtcpTransceiverImpl::CreateCompoundPacket(Timestamp now, size_t reserved_bytes, PacketSender& sender) { RTC_DCHECK(sender.IsEmpty()); + ReservedBytes reserved = {.per_packet = reserved_bytes}; absl::optional sdes; if (!config_.cname.empty()) { sdes.emplace(); bool added = sdes->AddCName(config_.feedback_ssrc, config_.cname); RTC_DCHECK(added) << "Failed to add CNAME " << config_.cname << " to RTCP SDES packet."; - reserved_bytes += sdes->BlockLength(); + reserved.per_packet += sdes->BlockLength(); } if (remb_.has_value()) { - reserved_bytes += remb_->BlockLength(); + reserved.per_packet += remb_->BlockLength(); + } + absl::optional xr_with_dlrr; + if (!received_rrtrs_.empty()) { + RTC_DCHECK(config_.reply_to_non_sender_rtt_measurement); + xr_with_dlrr.emplace(); + uint32_t now_ntp = + CompactNtp(config_.clock->ConvertTimestampToNtpTime(now)); + for (const auto& [ssrc, rrtr_info] : received_rrtrs_) { + rtcp::ReceiveTimeInfo reply; + reply.ssrc = ssrc; + reply.last_rr = rrtr_info.received_remote_mid_ntp_time; + reply.delay_since_last_rr = + now_ntp - rrtr_info.local_receive_mid_ntp_time; + xr_with_dlrr->AddDlrrItem(reply); + } + if (config_.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs) { + reserved.per_sender += xr_with_dlrr->BlockLength(); + } else { + reserved.per_packet += xr_with_dlrr->BlockLength(); + } } if (config_.non_sender_rtt_measurement) { + // It looks like bytes for ExtendedReport header are reserved twice, but in + // practice the same RtcpTransceiver won't both produce RRTR (i.e. it is a + // receiver-only) and reply to RRTR (i.e. remote participant is a receiver + // only). If that happen, then `reserved_bytes` would be slightly larger + // than it should, which is not an issue. + // 4 bytes for common RTCP header + 4 bytes for the ExtenedReports header. - reserved_bytes += (4 + 4 + rtcp::Rrtr::kLength); + reserved.per_packet += (4 + 4 + rtcp::Rrtr::kLength); } - CompoundPacketInfo result = FillReports(now, reserved_bytes, sender); + std::vector sender_ssrcs = FillReports(now, reserved, sender); + bool has_sender_report = !sender_ssrcs.empty(); + uint32_t sender_ssrc = + has_sender_report ? sender_ssrcs.front() : config_.feedback_ssrc; if (sdes.has_value() && !sender.IsEmpty()) { sender.AppendPacket(*sdes); } if (remb_.has_value()) { - remb_->SetSenderSsrc(result.sender_ssrc); + remb_->SetSenderSsrc(sender_ssrc); sender.AppendPacket(*remb_); } - if (!result.has_sender_report && config_.non_sender_rtt_measurement) { - rtcp::ExtendedReports xr; - xr.SetSenderSsrc(result.sender_ssrc); - + if (!has_sender_report && config_.non_sender_rtt_measurement) { + rtcp::ExtendedReports xr_with_rrtr; + xr_with_rrtr.SetSenderSsrc(config_.feedback_ssrc); rtcp::Rrtr rrtr; rrtr.SetNtp(config_.clock->ConvertTimestampToNtpTime(now)); - xr.SetRrtr(rrtr); - - sender.AppendPacket(xr); + xr_with_rrtr.SetRrtr(rrtr); + sender.AppendPacket(xr_with_rrtr); + } + if (xr_with_dlrr.has_value()) { + rtc::ArrayView ssrcs(&sender_ssrc, 1); + if (config_.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs && + !sender_ssrcs.empty()) { + ssrcs = sender_ssrcs; + } + RTC_DCHECK(!ssrcs.empty()); + for (uint32_t ssrc : ssrcs) { + xr_with_dlrr->SetSenderSsrc(ssrc); + sender.AppendPacket(*xr_with_dlrr); + } } } @@ -698,8 +840,8 @@ std::vector RtcpTransceiverImpl::CreateReportBlocks( const SenderReportTimes& last_sender_report = *it->second.last_received_sender_report; last_sr = CompactNtp(last_sender_report.remote_sent_time); - last_delay = SaturatedUsToCompactNtp( - now.us() - last_sender_report.local_received_time.us()); + last_delay = + SaturatedToCompactNtp(now - last_sender_report.local_received_time); report_block.SetLastSr(last_sr); report_block.SetDelayLastSr(last_delay); } diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h index ce25899bcf..8a3333d45c 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -82,6 +82,13 @@ class RtcpTransceiverImpl { class PacketSender; struct RemoteSenderState; struct LocalSenderState; + struct RrtrTimes { + // Received remote NTP timestamp in compact representation. + uint32_t received_remote_mid_ntp_time; + + // Local NTP time when the report was received in compact representation. + uint32_t local_receive_mid_ntp_time; + }; void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now, @@ -93,11 +100,20 @@ class RtcpTransceiverImpl { std::vector& report_blocks); void HandleReceiverReport(const rtcp::CommonHeader& rtcp_packet_header, std::vector& report_blocks); + void CallbackOnReportBlocks( + uint32_t sender_ssrc, + rtc::ArrayView report_blocks); void HandlePayloadSpecificFeedback( const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); void HandleRtpFeedback(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); + void HandleFir(const rtcp::CommonHeader& rtcp_packet_header); + void HandlePli(const rtcp::CommonHeader& rtcp_packet_header); + void HandleRemb(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); + void HandleNack(const rtcp::CommonHeader& rtcp_packet_header); + void HandleTransportFeedback(const rtcp::CommonHeader& rtcp_packet_header, + Timestamp now); void HandleExtendedReports(const rtcp::CommonHeader& rtcp_packet_header, Timestamp now); // Extended Reports blocks handlers. @@ -109,17 +125,18 @@ class RtcpTransceiverImpl { rtc::ArrayView report_blocks); void ReschedulePeriodicCompoundPackets(); - void SchedulePeriodicCompoundPackets(int64_t delay_ms); + void SchedulePeriodicCompoundPackets(TimeDelta delay); // Appends RTCP sender and receiver reports to the `sender`. // Both sender and receiver reports may have attached report blocks. - // Uses up to `config_.max_packet_size - reserved_bytes` - struct CompoundPacketInfo { - uint32_t sender_ssrc; - bool has_sender_report; + // Uses up to `config_.max_packet_size - reserved_bytes.per_packet` + // Returns list of sender ssrc in sender reports. + struct ReservedBytes { + size_t per_packet = 0; + size_t per_sender = 0; }; - CompoundPacketInfo FillReports(Timestamp now, - size_t reserved_bytes, - PacketSender& rtcp_sender); + std::vector FillReports(Timestamp now, + ReservedBytes reserved_bytes, + PacketSender& rtcp_sender); // Creates compound RTCP packet, as defined in // https://tools.ietf.org/html/rfc5506#section-2 @@ -144,6 +161,7 @@ class RtcpTransceiverImpl { std::list local_senders_; flat_map::iterator> local_senders_by_ssrc_; + flat_map received_rrtrs_; RepeatingTaskHandle periodic_task_handle_; }; diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc index 0a69d29df4..02c1e20b41 100644 --- a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -40,11 +40,13 @@ namespace { using ::testing::_; using ::testing::ElementsAre; +using ::testing::ElementsAreArray; using ::testing::Ge; using ::testing::NiceMock; using ::testing::Return; using ::testing::SizeIs; using ::testing::StrictMock; +using ::testing::UnorderedElementsAre; using ::testing::WithArg; using ::webrtc::rtcp::Bye; using ::webrtc::rtcp::CompoundPacket; @@ -69,7 +71,31 @@ class MockMediaReceiverRtcpObserver : public MediaReceiverRtcpObserver { class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler { public: + MockRtpStreamRtcpHandler() { + // With each next call increase number of sent packets and bytes to simulate + // active RTP sender. + ON_CALL(*this, SentStats).WillByDefault([this] { + RtpStats stats; + stats.set_num_sent_packets(++num_calls_); + stats.set_num_sent_bytes(1'000 * num_calls_); + return stats; + }); + } + MOCK_METHOD(RtpStats, SentStats, (), (override)); + MOCK_METHOD(void, + OnNack, + (uint32_t, rtc::ArrayView), + (override)); + MOCK_METHOD(void, OnFir, (uint32_t), (override)); + MOCK_METHOD(void, OnPli, (uint32_t), (override)); + MOCK_METHOD(void, + OnReportBlock, + (uint32_t, const rtcp::ReportBlock&), + (override)); + + private: + int num_calls_ = 0; }; class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { @@ -96,12 +122,18 @@ class MockNetworkLinkRtcpObserver : public NetworkLinkRtcpObserver { // Since some tests will need to wait for this period, make it small to avoid // slowing tests too much. As long as there are test bots with high scheduler // granularity, small period should be ok. -constexpr int kReportPeriodMs = 10; +constexpr TimeDelta kReportPeriod = TimeDelta::Millis(10); // On some systems task queue might be slow, instead of guessing right // grace period, use very large timeout, 100x larger expected wait time. // Use finite timeout to fail tests rather than hang them. constexpr int kAlmostForeverMs = 1000; +constexpr TimeDelta kTimePrecision = TimeDelta::Millis(1); + +MATCHER_P(Near, value, "") { + return arg > value - kTimePrecision && arg < value + kTimePrecision; +} + // Helper to wait for an rtcp packet produced on a different thread/task queue. class FakeRtcpTransport : public webrtc::Transport { public: @@ -161,8 +193,8 @@ RtcpTransceiverConfig DefaultTestConfig() { config.clock = &null_clock; config.outgoing_transport = &null_transport; config.schedule_periodic_compound_packets = false; - config.initial_report_delay_ms = 10; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Millis(10); + config.report_period = kReportPeriod; return config; } @@ -231,7 +263,7 @@ TEST(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 10; + config.initial_report_delay = TimeDelta::Millis(10); config.task_queue = queue.Get(); absl::optional rtcp_transceiver; @@ -239,7 +271,7 @@ TEST(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) { queue.PostTask([&] { rtcp_transceiver.emplace(config); }); EXPECT_TRUE(transport.WaitPacket()); - EXPECT_GE(rtc::TimeMillis() - started_ms, config.initial_report_delay_ms); + EXPECT_GE(rtc::TimeMillis() - started_ms, config.initial_report_delay.ms()); // Cleanup. rtc::Event done; @@ -258,8 +290,8 @@ TEST(RtcpTransceiverImplTest, PeriodicallySendsPackets) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 0; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; config.task_queue = queue.Get(); absl::optional rtcp_transceiver; int64_t time_just_before_1st_packet_ms = 0; @@ -275,7 +307,7 @@ TEST(RtcpTransceiverImplTest, PeriodicallySendsPackets) { int64_t time_just_after_2nd_packet_ms = rtc::TimeMillis(); EXPECT_GE(time_just_after_2nd_packet_ms - time_just_before_1st_packet_ms, - config.report_period_ms - 1); + config.report_period.ms() - 1); // Cleanup. rtc::Event done; @@ -294,8 +326,8 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { RtcpTransceiverConfig config; config.clock = &clock; config.outgoing_transport = &transport; - config.initial_report_delay_ms = 0; - config.report_period_ms = kReportPeriodMs; + config.initial_report_delay = TimeDelta::Zero(); + config.report_period = kReportPeriod; config.task_queue = queue.Get(); absl::optional rtcp_transceiver; queue.PostTask([&] { rtcp_transceiver.emplace(config); }); @@ -311,7 +343,7 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { rtcp_transceiver->SendCompoundPacket(); non_periodic.Set(); }, - config.report_period_ms / 2); + (config.report_period / 2).ms()); // Though non-periodic packet is scheduled just in between periodic, due to // small period and task queue flakiness it migth end-up 1ms after next // periodic packet. To be sure duration after non-periodic packet is tested @@ -323,7 +355,7 @@ TEST(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) { int64_t time_of_last_periodic_packet_ms = rtc::TimeMillis(); EXPECT_GE(time_of_last_periodic_packet_ms - time_of_non_periodic_packet_ms, - config.report_period_ms - 1); + config.report_period.ms() - 1); // Cleanup. rtc::Event done; @@ -946,10 +978,12 @@ TEST(RtcpTransceiverImplTest, // match result of ReceiveStatisticsProvider::RtcpReportBlocks callback, // but for simplicity of the test asume it is the same. ASSERT_EQ(report_blocks[0].source_ssrc(), kRemoteSsrc1); - EXPECT_EQ(CompactNtpRttToMs(report_blocks[0].delay_since_last_sr()), 200); + EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[0].delay_since_last_sr()), + Near(TimeDelta::Millis(200))); ASSERT_EQ(report_blocks[1].source_ssrc(), kRemoteSsrc2); - EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100); + EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[1].delay_since_last_sr()), + Near(TimeDelta::Millis(100))); } TEST(RtcpTransceiverImplTest, MaySendMultipleReceiverReportInSinglePacket) { @@ -1029,6 +1063,30 @@ TEST(RtcpTransceiverImplTest, SendsNack) { EXPECT_EQ(rtcp_parser.nack()->packet_ids(), kMissingSequenceNumbers); } +TEST(RtcpTransceiverImplTest, ReceivesNack) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + std::vector kMissingSequenceNumbers = {34, 37, 38}; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, + OnNack(kRemoteSsrc, ElementsAreArray(kMissingSequenceNumbers))); + EXPECT_CALL(local_stream2, OnNack).Times(0); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Nack nack; + nack.SetSenderSsrc(kRemoteSsrc); + nack.SetMediaSsrc(kMediaSsrc1); + nack.SetPacketIds(kMissingSequenceNumbers); + rtcp_transceiver.ReceivePacket(nack.Build(), config.clock->CurrentTime()); +} + TEST(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrc = 4321; @@ -1050,6 +1108,27 @@ TEST(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) { EXPECT_EQ(rtcp_parser.pli()->media_ssrc(), kRemoteSsrc); } +TEST(RtcpTransceiverImplTest, ReceivesPictureLossIndication) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, OnPli(kRemoteSsrc)); + EXPECT_CALL(local_stream2, OnPli).Times(0); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Pli pli; + pli.SetSenderSsrc(kRemoteSsrc); + pli.SetMediaSsrc(kMediaSsrc1); + rtcp_transceiver.ReceivePacket(pli.Build(), config.clock->CurrentTime()); +} + TEST(RtcpTransceiverImplTest, RequestKeyFrameWithFullIntraRequest) { const uint32_t kSenderSsrc = 1234; const uint32_t kRemoteSsrcs[] = {4321, 5321}; @@ -1130,6 +1209,83 @@ TEST(RtcpTransceiverImplTest, SendFirDoesNotIncreaseSeqNoIfOldRequest) { EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1); } +TEST(RtcpTransceiverImplTest, ReceivesFir) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc)); + EXPECT_CALL(local_stream2, OnFir).Times(0); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Fir fir; + fir.SetSenderSsrc(kRemoteSsrc); + fir.AddRequestTo(kMediaSsrc1, /*seq_num=*/13); + + rtcp_transceiver.ReceivePacket(fir.Build(), config.clock->CurrentTime()); +} + +TEST(RtcpTransceiverImplTest, IgnoresReceivedFirWithRepeatedSequenceNumber) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kMediaSsrc1 = 1234; + static constexpr uint32_t kMediaSsrc2 = 1235; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream2; + EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc)).Times(1); + EXPECT_CALL(local_stream2, OnFir(kRemoteSsrc)).Times(2); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2)); + + rtcp::Fir fir1; + fir1.SetSenderSsrc(kRemoteSsrc); + fir1.AddRequestTo(kMediaSsrc1, /*seq_num=*/132); + fir1.AddRequestTo(kMediaSsrc2, /*seq_num=*/10); + rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime()); + + // Repeat request for MediaSsrc1 - expect it to be ignored, + // Change FIR sequence number for MediaSsrc2 - expect a 2nd callback. + rtcp::Fir fir2; + fir2.SetSenderSsrc(kRemoteSsrc); + fir2.AddRequestTo(kMediaSsrc1, /*seq_num=*/132); + fir2.AddRequestTo(kMediaSsrc2, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime()); +} + +TEST(RtcpTransceiverImplTest, ReceivedFirTracksSequenceNumberPerRemoteSsrc) { + static constexpr uint32_t kRemoteSsrc1 = 4321; + static constexpr uint32_t kRemoteSsrc2 = 4323; + static constexpr uint32_t kMediaSsrc = 1234; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream; + EXPECT_CALL(local_stream, OnFir(kRemoteSsrc1)); + EXPECT_CALL(local_stream, OnFir(kRemoteSsrc2)); + + EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc, &local_stream)); + + rtcp::Fir fir1; + fir1.SetSenderSsrc(kRemoteSsrc1); + fir1.AddRequestTo(kMediaSsrc, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime()); + + // Use the same FIR sequence number, but different sender SSRC. + rtcp::Fir fir2; + fir2.SetSenderSsrc(kRemoteSsrc2); + fir2.AddRequestTo(kMediaSsrc, /*seq_num=*/13); + rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime()); +} + TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) { const uint32_t kRemoteSsrcs[] = {4321}; SimulatedClock clock(0); @@ -1194,6 +1350,105 @@ TEST(RtcpTransceiverImplTest, SendsXrRrtrWhenEnabled) { EXPECT_EQ(rtcp_parser.xr()->rrtr()->ntp(), ntp_time_now); } +TEST(RtcpTransceiverImplTest, RepliesToRrtrWhenEnabled) { + static constexpr uint32_t kSenderSsrc[] = {4321, 9876}; + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.reply_to_non_sender_rtt_measurement = true; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp::ExtendedReports xr; + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kSenderSsrc[0]); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(1'500)); + + rrtr.SetNtp(NtpTime(uint64_t{0x4444'5555'6666'7777})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kSenderSsrc[1]); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(500)); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); + static constexpr uint32_t kComactNtpOneSecond = 0x0001'0000; + EXPECT_THAT(rtcp_parser.xr()->dlrr().sub_blocks(), + UnorderedElementsAre( + rtcp::ReceiveTimeInfo(kSenderSsrc[0], 0x2222'3333, + /*delay=*/2 * kComactNtpOneSecond), + rtcp::ReceiveTimeInfo(kSenderSsrc[1], 0x5555'6666, + /*delay=*/kComactNtpOneSecond / 2))); +} + +TEST(RtcpTransceiverImplTest, CanReplyToRrtrOnceForAllLocalSsrcs) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kLocalSsrcs[] = {1234, 5678}; + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.reply_to_non_sender_rtt_measurement = true; + config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = false; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_sender0; + MockRtpStreamRtcpHandler local_sender1; + rtcp_transceiver.AddMediaSender(kLocalSsrcs[0], &local_sender0); + rtcp_transceiver.AddMediaSender(kLocalSsrcs[1], &local_sender1); + + rtcp::ExtendedReports xr; + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kRemoteSsrc); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(1'500)); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1); +} + +TEST(RtcpTransceiverImplTest, CanReplyToRrtrForEachLocalSsrc) { + static constexpr uint32_t kRemoteSsrc = 4321; + static constexpr uint32_t kLocalSsrc[] = {1234, 5678}; + SimulatedClock clock(0); + RtcpTransceiverConfig config = DefaultTestConfig(); + config.clock = &clock; + config.reply_to_non_sender_rtt_measurement = true; + config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = true; + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_sender0; + MockRtpStreamRtcpHandler local_sender1; + rtcp_transceiver.AddMediaSender(kLocalSsrc[0], &local_sender0); + rtcp_transceiver.AddMediaSender(kLocalSsrc[1], &local_sender1); + + rtcp::ExtendedReports xr; + rtcp::Rrtr rrtr; + rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444})); + xr.SetRrtr(rrtr); + xr.SetSenderSsrc(kRemoteSsrc); + rtcp_transceiver.ReceivePacket(xr.Build(), clock.CurrentTime()); + clock.AdvanceTime(TimeDelta::Millis(1'500)); + + rtcp_transceiver.SendCompoundPacket(); + + EXPECT_EQ(rtcp_parser.xr()->num_packets(), 2); +} + TEST(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) { SimulatedClock clock(0); RtcpTransceiverConfig config; @@ -1227,11 +1482,12 @@ TEST(RtcpTransceiverImplTest, PassRttFromDlrrToLinkObserver) { rtcp::ReceiveTimeInfo rti; rti.ssrc = kSenderSsrc; rti.last_rr = CompactNtp(config.clock->ConvertTimestampToNtpTime(send_time)); - rti.delay_since_last_rr = SaturatedUsToCompactNtp(10'000); // 10ms + rti.delay_since_last_rr = SaturatedToCompactNtp(TimeDelta::Millis(10)); rtcp::ExtendedReports xr; xr.AddDlrrItem(rti); - EXPECT_CALL(link_observer, OnRttUpdate(receive_time, TimeDelta::Millis(100))); + EXPECT_CALL(link_observer, + OnRttUpdate(receive_time, Near(TimeDelta::Millis(100)))); rtcp_transceiver.ReceivePacket(xr.Build(), receive_time); } @@ -1248,15 +1504,15 @@ TEST(RtcpTransceiverImplTest, CalculatesRoundTripTimeFromReportBlocks) { rtcp::ReportBlock rb1; rb1.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime( receive_time - rtt - TimeDelta::Millis(10)))); - rb1.SetDelayLastSr(SaturatedUsToCompactNtp(10'000)); // 10ms + rb1.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(10))); rr.AddReportBlock(rb1); rtcp::ReportBlock rb2; rb2.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime( receive_time - rtt - TimeDelta::Millis(20)))); - rb2.SetDelayLastSr(SaturatedUsToCompactNtp(20'000)); // 20ms + rb2.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(20))); rr.AddReportBlock(rb2); - EXPECT_CALL(link_observer, OnRttUpdate(receive_time, rtt)); + EXPECT_CALL(link_observer, OnRttUpdate(receive_time, Near(rtt))); rtcp_transceiver.ReceivePacket(rr.Build(), receive_time); } @@ -1324,7 +1580,7 @@ TEST(RtcpTransceiverImplTest, ParsesRemb) { } TEST(RtcpTransceiverImplTest, - CombinesReportBlocksFromSenderAndRecieverReports) { + CombinesReportBlocksFromSenderAndReceiverReports) { MockNetworkLinkRtcpObserver link_observer; RtcpTransceiverConfig config = DefaultTestConfig(); config.network_link_observer = &link_observer; @@ -1349,6 +1605,54 @@ TEST(RtcpTransceiverImplTest, rtcp_transceiver.ReceivePacket(packet.Build(), receive_time); } +TEST(RtcpTransceiverImplTest, + CallbackOnReportBlocksFromSenderAndReceiverReports) { + static constexpr uint32_t kRemoteSsrc = 5678; + // Has registered sender, report block attached to sender report. + static constexpr uint32_t kMediaSsrc1 = 1234; + // No registered sender, report block attached to receiver report. + // Such report block shouldn't prevent handling following report block. + static constexpr uint32_t kMediaSsrc2 = 1235; + // Has registered sender, no report block attached. + static constexpr uint32_t kMediaSsrc3 = 1236; + // Has registered sender, report block attached to receiver report. + static constexpr uint32_t kMediaSsrc4 = 1237; + + MockNetworkLinkRtcpObserver link_observer; + RtcpTransceiverConfig config = DefaultTestConfig(); + Timestamp receive_time = Timestamp::Seconds(5678); + RtcpTransceiverImpl rtcp_transceiver(config); + + MockRtpStreamRtcpHandler local_stream1; + MockRtpStreamRtcpHandler local_stream3; + MockRtpStreamRtcpHandler local_stream4; + EXPECT_CALL(local_stream1, OnReportBlock(kRemoteSsrc, _)); + EXPECT_CALL(local_stream3, OnReportBlock).Times(0); + EXPECT_CALL(local_stream4, OnReportBlock(kRemoteSsrc, _)); + + ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1)); + ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc3, &local_stream3)); + ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc4, &local_stream4)); + + // Assemble compound packet with multiple RTCP packets in it. + rtcp::CompoundPacket packet; + auto sr = std::make_unique(); + sr->SetSenderSsrc(kRemoteSsrc); + std::vector rb(1); + rb[0].SetMediaSsrc(kMediaSsrc1); + sr->SetReportBlocks(std::move(rb)); + packet.Append(std::move(sr)); + auto rr = std::make_unique(); + rr->SetSenderSsrc(kRemoteSsrc); + rb = std::vector(2); + rb[0].SetMediaSsrc(kMediaSsrc2); + rb[1].SetMediaSsrc(kMediaSsrc4); + rr->SetReportBlocks(std::move(rb)); + packet.Append(std::move(rr)); + + rtcp_transceiver.ReceivePacket(packet.Build(), receive_time); +} + TEST(RtcpTransceiverImplTest, FailsToRegisterTwoSendersWithTheSameSsrc) { RtcpTransceiverImpl rtcp_transceiver(DefaultTestConfig()); MockRtpStreamRtcpHandler sender1; @@ -1472,5 +1776,56 @@ TEST(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) { } } +TEST(RtcpTransceiverImplTest, SkipsSenderReportForInactiveSender) { + static constexpr uint32_t kSenderSsrc[] = {12345, 23456}; + RtcpTransceiverConfig config = DefaultTestConfig(); + RtcpPacketParser rtcp_parser; + RtcpParserTransport transport(&rtcp_parser); + config.outgoing_transport = &transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + RtpStreamRtcpHandler::RtpStats sender_stats[2]; + NiceMock sender[2]; + ON_CALL(sender[0], SentStats).WillByDefault([&] { return sender_stats[0]; }); + ON_CALL(sender[1], SentStats).WillByDefault([&] { return sender_stats[1]; }); + rtcp_transceiver.AddMediaSender(kSenderSsrc[0], &sender[0]); + rtcp_transceiver.AddMediaSender(kSenderSsrc[1], &sender[1]); + + // Start with both senders beeing active. + sender_stats[0].set_num_sent_packets(10); + sender_stats[0].set_num_sent_bytes(1'000); + sender_stats[1].set_num_sent_packets(5); + sender_stats[1].set_num_sent_bytes(2'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 1); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 2); + + // Keep 1st sender active, but make 2nd second look inactive by returning the + // same RtpStats. + sender_stats[0].set_num_sent_packets(15); + sender_stats[0].set_num_sent_bytes(2'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 2); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 3); + EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[0]); + + // Swap active sender. + sender_stats[1].set_num_sent_packets(20); + sender_stats[1].set_num_sent_bytes(3'000); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 3); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 4); + EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[1]); + + // Activate both senders again. + sender_stats[0].set_num_sent_packets(20); + sender_stats[0].set_num_sent_bytes(3'000); + sender_stats[1].set_num_sent_packets(25); + sender_stats[1].set_num_sent_bytes(3'500); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_EQ(transport.num_packets(), 4); + EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6); +} + } // namespace } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_history.cc b/modules/rtp_rtcp/source/rtp_packet_history.cc index fe5ccc708e..c8d400a985 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history.cc @@ -23,26 +23,13 @@ namespace webrtc { -constexpr size_t RtpPacketHistory::kMaxCapacity; -constexpr size_t RtpPacketHistory::kMaxPaddingHistory; -constexpr int64_t RtpPacketHistory::kMinPacketDurationMs; -constexpr int RtpPacketHistory::kMinPacketDurationRtt; -constexpr int RtpPacketHistory::kPacketCullingDelayFactor; - -RtpPacketHistory::PacketState::PacketState() = default; -RtpPacketHistory::PacketState::PacketState(const PacketState&) = default; -RtpPacketHistory::PacketState::~PacketState() = default; - RtpPacketHistory::StoredPacket::StoredPacket( std::unique_ptr packet, - absl::optional send_time_ms, + Timestamp send_time, uint64_t insert_order) - : send_time_ms_(send_time_ms), - packet_(std::move(packet)), - // No send time indicates packet is not sent immediately, but instead will - // be put in the pacer queue and later retrieved via - // GetPacketAndSetSendTime(). - pending_transmission_(!send_time_ms.has_value()), + : packet_(std::move(packet)), + pending_transmission_(false), + send_time_(send_time), insert_order_(insert_order), times_retransmitted_(0) {} @@ -85,7 +72,7 @@ RtpPacketHistory::RtpPacketHistory(Clock* clock, bool enable_padding_prio) enable_padding_prio_(enable_padding_prio), number_to_store_(0), mode_(StorageMode::kDisabled), - rtt_ms_(-1), + rtt_(TimeDelta::MinusInfinity()), packets_inserted_(0) {} RtpPacketHistory::~RtpPacketHistory() {} @@ -107,29 +94,28 @@ RtpPacketHistory::StorageMode RtpPacketHistory::GetStorageMode() const { return mode_; } -void RtpPacketHistory::SetRtt(int64_t rtt_ms) { +void RtpPacketHistory::SetRtt(TimeDelta rtt) { MutexLock lock(&lock_); - RTC_DCHECK_GE(rtt_ms, 0); - rtt_ms_ = rtt_ms; + RTC_DCHECK_GE(rtt, TimeDelta::Zero()); + rtt_ = rtt; // If storage is not disabled, packets will be removed after a timeout // that depends on the RTT. Changing the RTT may thus cause some packets // become "old" and subject to removal. if (mode_ != StorageMode::kDisabled) { - CullOldPackets(clock_->TimeInMilliseconds()); + CullOldPackets(); } } void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, - absl::optional send_time_ms) { + Timestamp send_time) { RTC_DCHECK(packet); MutexLock lock(&lock_); - int64_t now_ms = clock_->TimeInMilliseconds(); if (mode_ == StorageMode::kDisabled) { return; } RTC_DCHECK(packet->allow_retransmission()); - CullOldPackets(now_ms); + CullOldPackets(); // Store packet. const uint16_t rtp_seq_no = packet->SequenceNumber(); @@ -145,11 +131,11 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, // Packet to be inserted ahead of first packet, expand front. for (; packet_index < 0; ++packet_index) { - packet_history_.emplace_front(nullptr, absl::nullopt, 0); + packet_history_.emplace_front(); } // Packet to be inserted behind last packet, expand back. while (static_cast(packet_history_.size()) <= packet_index) { - packet_history_.emplace_back(nullptr, absl::nullopt, 0); + packet_history_.emplace_back(); } RTC_DCHECK_GE(packet_index, 0); @@ -157,7 +143,7 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, RTC_DCHECK(packet_history_[packet_index].packet_ == nullptr); packet_history_[packet_index] = - StoredPacket(std::move(packet), send_time_ms, packets_inserted_++); + StoredPacket(std::move(packet), send_time, packets_inserted_++); if (enable_padding_prio_) { if (padding_priority_.size() >= kMaxPaddingHistory - 1) { @@ -168,36 +154,6 @@ void RtpPacketHistory::PutRtpPacket(std::unique_ptr packet, } } -std::unique_ptr RtpPacketHistory::GetPacketAndSetSendTime( - uint16_t sequence_number) { - MutexLock lock(&lock_); - if (mode_ == StorageMode::kDisabled) { - return nullptr; - } - - StoredPacket* packet = GetStoredPacket(sequence_number); - if (packet == nullptr) { - return nullptr; - } - - int64_t now_ms = clock_->TimeInMilliseconds(); - if (!VerifyRtt(*packet, now_ms)) { - return nullptr; - } - - if (packet->send_time_ms_) { - packet->IncrementTimesRetransmitted( - enable_padding_prio_ ? &padding_priority_ : nullptr); - } - - // Update send-time and mark as no long in pacer queue. - packet->send_time_ms_ = now_ms; - packet->pending_transmission_ = false; - - // Return copy of packet instance since it may need to be retransmitted. - return std::make_unique(*packet->packet_); -} - std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( uint16_t sequence_number) { return GetPacketAndMarkAsPending( @@ -225,7 +181,7 @@ std::unique_ptr RtpPacketHistory::GetPacketAndMarkAsPending( return nullptr; } - if (!VerifyRtt(*packet, clock_->TimeInMilliseconds())) { + if (!VerifyRtt(*packet)) { // Packet already resent within too short a time window, ignore. return nullptr; } @@ -251,51 +207,45 @@ void RtpPacketHistory::MarkPacketAsSent(uint16_t sequence_number) { return; } - RTC_DCHECK(packet->send_time_ms_); - // Update send-time, mark as no longer in pacer queue, and increment // transmission count. - packet->send_time_ms_ = clock_->TimeInMilliseconds(); + packet->set_send_time(clock_->CurrentTime()); packet->pending_transmission_ = false; packet->IncrementTimesRetransmitted(enable_padding_prio_ ? &padding_priority_ : nullptr); } -absl::optional RtpPacketHistory::GetPacketState( - uint16_t sequence_number) const { +bool RtpPacketHistory::GetPacketState(uint16_t sequence_number) const { MutexLock lock(&lock_); if (mode_ == StorageMode::kDisabled) { - return absl::nullopt; + return false; } int packet_index = GetPacketIndex(sequence_number); if (packet_index < 0 || static_cast(packet_index) >= packet_history_.size()) { - return absl::nullopt; + return false; } const StoredPacket& packet = packet_history_[packet_index]; if (packet.packet_ == nullptr) { - return absl::nullopt; + return false; } - if (!VerifyRtt(packet, clock_->TimeInMilliseconds())) { - return absl::nullopt; + if (!VerifyRtt(packet)) { + return false; } - return StoredPacketToPacketState(packet); + return true; } -bool RtpPacketHistory::VerifyRtt(const RtpPacketHistory::StoredPacket& packet, - int64_t now_ms) const { - if (packet.send_time_ms_) { - // Send-time already set, this check must be for a retransmission. - if (packet.times_retransmitted() > 0 && - now_ms < *packet.send_time_ms_ + rtt_ms_) { - // This packet has already been retransmitted once, and the time since - // that even is lower than on RTT. Ignore request as this packet is - // likely already in the network pipe. - return false; - } +bool RtpPacketHistory::VerifyRtt( + const RtpPacketHistory::StoredPacket& packet) const { + if (packet.times_retransmitted() > 0 && + clock_->CurrentTime() - packet.send_time() < rtt_) { + // This packet has already been retransmitted once, and the time since + // that even is lower than on RTT. Ignore request as this packet is + // likely already in the network pipe. + return false; } return true; @@ -348,7 +298,7 @@ std::unique_ptr RtpPacketHistory::GetPayloadPaddingPacket( return nullptr; } - best_packet->send_time_ms_ = clock_->TimeInMilliseconds(); + best_packet->set_send_time(clock_->CurrentTime()); best_packet->IncrementTimesRetransmitted( enable_padding_prio_ ? &padding_priority_ : nullptr); @@ -368,21 +318,6 @@ void RtpPacketHistory::CullAcknowledgedPackets( } } -bool RtpPacketHistory::SetPendingTransmission(uint16_t sequence_number) { - MutexLock lock(&lock_); - if (mode_ == StorageMode::kDisabled) { - return false; - } - - StoredPacket* packet = GetStoredPacket(sequence_number); - if (packet == nullptr) { - return false; - } - - packet->pending_transmission_ = true; - return true; -} - void RtpPacketHistory::Clear() { MutexLock lock(&lock_); Reset(); @@ -393,9 +328,12 @@ void RtpPacketHistory::Reset() { padding_priority_.clear(); } -void RtpPacketHistory::CullOldPackets(int64_t now_ms) { - int64_t packet_duration_ms = - std::max(kMinPacketDurationRtt * rtt_ms_, kMinPacketDurationMs); +void RtpPacketHistory::CullOldPackets() { + Timestamp now = clock_->CurrentTime(); + TimeDelta packet_duration = + rtt_.IsFinite() + ? std::max(kMinPacketDurationRtt * rtt_, kMinPacketDuration) + : kMinPacketDuration; while (!packet_history_.empty()) { if (packet_history_.size() >= kMaxCapacity) { // We have reached the absolute max capacity, remove one packet @@ -410,15 +348,15 @@ void RtpPacketHistory::CullOldPackets(int64_t now_ms) { return; } - if (*stored_packet.send_time_ms_ + packet_duration_ms > now_ms) { + if (stored_packet.send_time() + packet_duration > now) { // Don't cull packets too early to avoid failed retransmission requests. return; } if (packet_history_.size() >= number_to_store_ || - *stored_packet.send_time_ms_ + - (packet_duration_ms * kPacketCullingDelayFactor) <= - now_ms) { + stored_packet.send_time() + + (packet_duration * kPacketCullingDelayFactor) <= + now) { // Too many packets in history, or this packet has timed out. Remove it // and continue. RemovePacket(0); @@ -487,17 +425,4 @@ RtpPacketHistory::StoredPacket* RtpPacketHistory::GetStoredPacket( return &packet_history_[index]; } -RtpPacketHistory::PacketState RtpPacketHistory::StoredPacketToPacketState( - const RtpPacketHistory::StoredPacket& stored_packet) { - RtpPacketHistory::PacketState state; - state.rtp_sequence_number = stored_packet.packet_->SequenceNumber(); - state.send_time_ms = stored_packet.send_time_ms_; - state.capture_time_ms = stored_packet.packet_->capture_time_ms(); - state.ssrc = stored_packet.packet_->Ssrc(); - state.packet_size = stored_packet.packet_->size(); - state.times_retransmitted = stored_packet.times_retransmitted(); - state.pending_transmission = stored_packet.pending_transmission_; - return state; -} - } // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtp_packet_history.h b/modules/rtp_rtcp/source/rtp_packet_history.h index f87ad4d550..7475a35be3 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history.h +++ b/modules/rtp_rtcp/source/rtp_packet_history.h @@ -15,9 +15,12 @@ #include #include #include +#include #include #include "api/function_view.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -35,28 +38,12 @@ class RtpPacketHistory { // packets as they time out or as signaled as received. }; - // Snapshot indicating the state of a packet in the history. - struct PacketState { - PacketState(); - PacketState(const PacketState&); - ~PacketState(); - - uint16_t rtp_sequence_number = 0; - absl::optional send_time_ms; - int64_t capture_time_ms = 0; - uint32_t ssrc = 0; - size_t packet_size = 0; - // Number of times RE-transmitted, ie not including the first transmission. - size_t times_retransmitted = 0; - bool pending_transmission = false; - }; - // Maximum number of packets we ever allow in the history. static constexpr size_t kMaxCapacity = 9600; // Maximum number of entries in prioritized queue of padding packets. static constexpr size_t kMaxPaddingHistory = 63; - // Don't remove packets within max(1000ms, 3x RTT). - static constexpr int64_t kMinPacketDurationMs = 1000; + // Don't remove packets within max(1 second, 3x RTT). + static constexpr TimeDelta kMinPacketDuration = TimeDelta::Seconds(1); static constexpr int kMinPacketDurationRtt = 3; // With kStoreAndCull, always remove packets after 3x max(1000ms, 3x rtt). static constexpr int kPacketCullingDelayFactor = 3; @@ -76,17 +63,10 @@ class RtpPacketHistory { // Set RTT, used to avoid premature retransmission and to prevent over-writing // a packet in the history before we are reasonably sure it has been received. - void SetRtt(int64_t rtt_ms); + void SetRtt(TimeDelta rtt); - // If `send_time` is set, packet was sent without using pacer, so state will - // be set accordingly. void PutRtpPacket(std::unique_ptr packet, - absl::optional send_time_ms); - - // Gets stored RTP packet corresponding to the input |sequence number|. - // Returns nullptr if packet is not found or was (re)sent too recently. - std::unique_ptr GetPacketAndSetSendTime( - uint16_t sequence_number); + Timestamp send_time); // Gets stored RTP packet corresponding to the input |sequence number|. // Returns nullptr if packet is not found or was (re)sent too recently. @@ -109,9 +89,9 @@ class RtpPacketHistory { // counter. Marks the packet as no longer being in the pacer queue. void MarkPacketAsSent(uint16_t sequence_number); - // Similar to GetPacketAndSetSendTime(), but only returns a snapshot of the - // current state for packet, and never updates internal state. - absl::optional GetPacketState(uint16_t sequence_number) const; + // Returns true if history contains packet with `sequence_number` and it can + // be retransmitted. + bool GetPacketState(uint16_t sequence_number) const; // Get the packet (if any) from the history, that is deemed most likely to // the remote side. This is calculated from heuristics such as packet age @@ -130,11 +110,6 @@ class RtpPacketHistory { // Cull packets that have been acknowledged as received by the remote end. void CullAcknowledgedPackets(rtc::ArrayView sequence_numbers); - // Mark packet as queued for transmission. This will prevent premature - // removal or duplicate retransmissions in the pacer queue. - // Returns true if status was set, false if packet was not found. - bool SetPendingTransmission(uint16_t sequence_number); - // Remove all pending packets from the history, but keep storage mode and // capacity. void Clear(); @@ -146,8 +121,9 @@ class RtpPacketHistory { class StoredPacket { public: + StoredPacket() = default; StoredPacket(std::unique_ptr packet, - absl::optional send_time_ms, + Timestamp send_time, uint64_t insert_order); StoredPacket(StoredPacket&&); StoredPacket& operator=(StoredPacket&&); @@ -158,7 +134,8 @@ class RtpPacketHistory { void IncrementTimesRetransmitted(PacketPrioritySet* priority_set); // The time of last transmission, including retransmissions. - absl::optional send_time_ms_; + Timestamp send_time() const { return send_time_; } + void set_send_time(Timestamp value) { send_time_ = value; } // The actual packet. std::unique_ptr packet_; @@ -167,6 +144,8 @@ class RtpPacketHistory { bool pending_transmission_; private: + Timestamp send_time_ = Timestamp::Zero(); + // Unique number per StoredPacket, incremented by one for each added // packet. Used to sort on insert order. uint64_t insert_order_; @@ -178,12 +157,11 @@ class RtpPacketHistory { bool operator()(StoredPacket* lhs, StoredPacket* rhs) const; }; - // Helper method used by GetPacketAndSetSendTime() and GetPacketState() to - // check if packet has too recently been sent. - bool VerifyRtt(const StoredPacket& packet, int64_t now_ms) const + // Helper method to check if packet has too recently been sent. + bool VerifyRtt(const StoredPacket& packet) const RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); void Reset() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); - void CullOldPackets(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); + void CullOldPackets() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); // Removes the packet from the history, and context/mapping that has been // stored. Returns the RTP packet instance contained within the StoredPacket. std::unique_ptr RemovePacket(int packet_index) @@ -192,15 +170,13 @@ class RtpPacketHistory { RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); StoredPacket* GetStoredPacket(uint16_t sequence_number) RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_); - static PacketState StoredPacketToPacketState( - const StoredPacket& stored_packet); Clock* const clock_; const bool enable_padding_prio_; mutable Mutex lock_; size_t number_to_store_ RTC_GUARDED_BY(lock_); StorageMode mode_ RTC_GUARDED_BY(lock_); - int64_t rtt_ms_ RTC_GUARDED_BY(lock_); + TimeDelta rtt_ RTC_GUARDED_BY(lock_); // Queue of stored packets, ordered by sequence number, with older packets in // the front and new packets being added to the back. Note that there may be diff --git a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc index 90e984a78c..f50541849e 100644 --- a/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_packet_history_unittest.cc @@ -45,7 +45,7 @@ class RtpPacketHistoryTest : public ::testing::TestWithParam { // Payload, ssrc, timestamp and extensions are irrelevant for this tests. std::unique_ptr packet(new RtpPacketToSend(nullptr)); packet->SetSequenceNumber(seq_num); - packet->set_capture_time_ms(fake_clock_.TimeInMilliseconds()); + packet->set_capture_time(fake_clock_.CurrentTime()); packet->set_allow_retransmission(true); return packet; } @@ -63,8 +63,8 @@ TEST_P(RtpPacketHistoryTest, SetStoreStatus) { TEST_P(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - // Store a packet, but with send-time. It should then not be removed. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), absl::nullopt); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Changing store status, even to the current one, will clear the history. @@ -74,25 +74,27 @@ TEST_P(RtpPacketHistoryTest, ClearsHistoryAfterSetStoreStatus) { TEST_P(RtpPacketHistoryTest, StartSeqResetAfterReset) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - // Store a packet, but with send-time. It should then not be removed. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), absl::nullopt); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), + /*send_time=*/fake_clock_.CurrentTime()); + // Mark packet as pending so it won't be removed. + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Changing store status, to clear the history. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); // Add a new packet. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), absl::nullopt); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), + /*send_time=*/fake_clock_.CurrentTime()); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + 1))); // Advance time past where packet expires. - fake_clock_.AdvanceTimeMilliseconds( - RtpPacketHistory::kPacketCullingDelayFactor * - RtpPacketHistory::kMinPacketDurationMs); + fake_clock_.AdvanceTime(RtpPacketHistory::kPacketCullingDelayFactor * + RtpPacketHistory::kMinPacketDuration); // Add one more packet and verify no state left from packet before reset. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), absl::nullopt); + hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); @@ -101,7 +103,8 @@ TEST_P(RtpPacketHistoryTest, StartSeqResetAfterReset) { TEST_P(RtpPacketHistoryTest, NoStoreStatus) { EXPECT_EQ(StorageMode::kDisabled, hist_.GetStorageMode()); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); // Packet should not be stored. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } @@ -116,139 +119,70 @@ TEST_P(RtpPacketHistoryTest, PutRtpPacket) { std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, GetRtpPacket) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - int64_t capture_time_ms = 1; + Timestamp capture_time = Timestamp::Millis(1); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(capture_time); rtc::CopyOnWriteBuffer buffer = packet->Buffer(); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); std::unique_ptr packet_out = - hist_.GetPacketAndSetSendTime(kStartSeqNum); - EXPECT_TRUE(packet_out); + hist_.GetPacketAndMarkAsPending(kStartSeqNum); + ASSERT_TRUE(packet_out); EXPECT_EQ(buffer, packet_out->Buffer()); - EXPECT_EQ(capture_time_ms, packet_out->capture_time_ms()); -} - -TEST_P(RtpPacketHistoryTest, PacketStateIsCorrect) { - const uint32_t kSsrc = 92384762; - const int64_t kRttMs = 100; - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kRttMs); - std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - packet->SetSsrc(kSsrc); - packet->SetPayloadSize(1234); - const size_t packet_size = packet->size(); - - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - - absl::optional state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(state); - EXPECT_EQ(state->rtp_sequence_number, kStartSeqNum); - EXPECT_EQ(state->send_time_ms, fake_clock_.TimeInMilliseconds()); - EXPECT_EQ(state->capture_time_ms, fake_clock_.TimeInMilliseconds()); - EXPECT_EQ(state->ssrc, kSsrc); - EXPECT_EQ(state->packet_size, packet_size); - EXPECT_EQ(state->times_retransmitted, 0u); - - fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - fake_clock_.AdvanceTimeMilliseconds(kRttMs + 1); - - state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(state); - EXPECT_EQ(state->times_retransmitted, 1u); + EXPECT_EQ(capture_time, packet_out->capture_time()); } -TEST_P(RtpPacketHistoryTest, MinResendTimeWithPacer) { - static const int64_t kMinRetransmitIntervalMs = 100; +TEST_P(RtpPacketHistoryTest, MinResendTime) { + static const TimeDelta kMinRetransmitInterval = TimeDelta::Millis(100); hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kMinRetransmitIntervalMs); - int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); + hist_.SetRtt(kMinRetransmitInterval); + Timestamp capture_time = fake_clock_.CurrentTime(); std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); size_t len = packet->size(); - hist_.PutRtpPacket(std::move(packet), absl::nullopt); - - // First transmission: TimeToSendPacket() call from pacer. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // First retransmission - allow early retransmission. fake_clock_.AdvanceTimeMilliseconds(1); - - // With pacer there's two calls to history: - // 1) When the NACK request arrived, use GetPacketState() to see if the - // packet is there and verify RTT constraints. Then we use the ssrc - // and sequence number to enqueue the retransmission in the pacer - // 2) When the pacer determines that it is time to send the packet, it calls - // GetPacketAndSetSendTime(). - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - EXPECT_TRUE(packet_state); - EXPECT_EQ(len, packet_state->packet_size); - EXPECT_EQ(capture_time_ms, packet_state->capture_time_ms); - - // Retransmission was allowed, next send it from pacer. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - - // Second retransmission - advance time to just before retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1); - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - - // Advance time to just after retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); -} - -TEST_P(RtpPacketHistoryTest, MinResendTimeWithoutPacer) { - static const int64_t kMinRetransmitIntervalMs = 100; - - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); - hist_.SetRtt(kMinRetransmitIntervalMs); - int64_t capture_time_ms = fake_clock_.TimeInMilliseconds(); - std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); - size_t len = packet->size(); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - - // First retransmission - allow early retransmission. - fake_clock_.AdvanceTimeMilliseconds(1); - packet = hist_.GetPacketAndSetSendTime(kStartSeqNum); - EXPECT_TRUE(packet); + packet = hist_.GetPacketAndMarkAsPending(kStartSeqNum); + ASSERT_TRUE(packet); EXPECT_EQ(len, packet->size()); - EXPECT_EQ(capture_time_ms, packet->capture_time_ms()); + EXPECT_EQ(packet->capture_time(), capture_time); + hist_.MarkPacketAsSent(kStartSeqNum); // Second retransmission - advance time to just before retransmission OK. - fake_clock_.AdvanceTimeMilliseconds(kMinRetransmitIntervalMs - 1); - EXPECT_FALSE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + fake_clock_.AdvanceTime(kMinRetransmitInterval - TimeDelta::Millis(1)); + EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Advance time to just after retransmission OK. fake_clock_.AdvanceTimeMilliseconds(1); - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) { const size_t kMaxNumPackets = 10; hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - // History does not allow removing packets within kMinPacketDurationMs, + // History does not allow removing packets within kMinPacketDuration, // so in order to test capacity, make sure insertion spans this time. - const int64_t kPacketIntervalMs = - RtpPacketHistory::kMinPacketDurationMs / kMaxNumPackets; + const TimeDelta kPacketInterval = + RtpPacketHistory::kMinPacketDuration / kMaxNumPackets; // Add packets until the buffer is full. for (size_t i = 0; i < kMaxNumPackets; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); // Immediate mark packet as sent. - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(kPacketIntervalMs); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(kPacketInterval); } // First packet should still be there. @@ -257,7 +191,7 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestSentPacketWhenAtMaxSize) { // History is full, oldest one should be overwritten. std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Oldest packet should be gone, but packet after than one still present. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); @@ -275,8 +209,10 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) { for (size_t i = 0; i < kMaxNumPackets; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); - // Don't mark packets as sent, preventing them from being removed. - hist_.PutRtpPacket(std::move(packet), absl::nullopt); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); + // Mark packets as pending, preventing it from being removed. + hist_.GetPacketAndMarkAsPending(To16u(kStartSeqNum + i)); } // First packet should still be there. @@ -285,7 +221,7 @@ TEST_P(RtpPacketHistoryTest, RemovesOldestPacketWhenAtMaxCapacity) { // History is full, oldest one should be overwritten. std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Oldest packet should be gone, but packet after than one still present. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); @@ -302,14 +238,14 @@ TEST_P(RtpPacketHistoryTest, RemovesLowestPrioPaddingWhenAtMaxCapacity) { // set of potential padding packets. const size_t kMaxNumPackets = RtpPacketHistory::kMaxPaddingHistory; hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets * 2); - hist_.SetRtt(1); + hist_.SetRtt(TimeDelta::Millis(1)); // Add packets until the max is reached, and then yet another one. for (size_t i = 0; i < kMaxNumPackets + 1; ++i) { std::unique_ptr packet = CreateRtpPacket(To16u(kStartSeqNum + i)); // Don't mark packets as sent, preventing them from being removed. - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); } // Advance time to allow retransmission/padding. @@ -329,88 +265,53 @@ TEST_P(RtpPacketHistoryTest, RemovesLowestPrioPaddingWhenAtMaxCapacity) { EXPECT_EQ(packet->SequenceNumber(), To16u(kStartSeqNum + kMaxNumPackets)); } -TEST_P(RtpPacketHistoryTest, DontRemoveUnsentPackets) { - const size_t kMaxNumPackets = 10; - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - - // Add packets until the buffer is full. - for (size_t i = 0; i < kMaxNumPackets; ++i) { - // Mark packets as unsent. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)), absl::nullopt); - } - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs); - - // First packet should still be there. - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - - // History is full, but old packets not sent, so allow expansion. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets)), - fake_clock_.TimeInMilliseconds()); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); - - // Set all packet as sent and advance time past min packet duration time, - // otherwise packets till still be prevented from being removed. - for (size_t i = 0; i <= kMaxNumPackets; ++i) { - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + i))); - } - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs); - // Add a new packet, this means the two oldest ones will be culled. - hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + kMaxNumPackets + 1)), - fake_clock_.TimeInMilliseconds()); - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); -} - TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPackets) { // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add a packet, marked as send, and advance time to just before removal time. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(RtpPacketHistory::kMinPacketDurationMs - - 1); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(RtpPacketHistory::kMinPacketDuration - + TimeDelta::Millis(1)); // Add a new packet to trigger culling. hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Advance time to where packet will be eligible for removal and try again. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should no be gone, but next one still there. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); } TEST_P(RtpPacketHistoryTest, DontRemoveTooRecentlyTransmittedPacketsHighRtt) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - const int64_t kPacketTimeoutMs = - kRttMs * RtpPacketHistory::kMinPacketDurationRtt; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + const TimeDelta kPacketTimeout = + kRtt * RtpPacketHistory::kMinPacketDurationRtt; // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); // Add a packet, marked as send, and advance time to just before removal time. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); - fake_clock_.AdvanceTimeMilliseconds(kPacketTimeoutMs - 1); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); + fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); // Add a new packet to trigger culling. hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Advance time to where packet will be eligible for removal and try again. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 2)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // First packet should no be gone, but next one still there. EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); @@ -421,12 +322,11 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCulling) { // Enable culling. Even without feedback, this can trigger early removal. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); - int64_t kMaxPacketDurationMs = RtpPacketHistory::kMinPacketDurationMs * + TimeDelta kMaxPacketDuration = RtpPacketHistory::kMinPacketDuration * RtpPacketHistory::kPacketCullingDelayFactor; - fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1); + fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); @@ -434,25 +334,24 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCulling) { // Advance to where packet can be culled, even if buffer is not full. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) { const size_t kMaxNumPackets = 10; - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; // Enable culling. Even without feedback, this can trigger early removal. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, kMaxNumPackets); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); - int64_t kMaxPacketDurationMs = kRttMs * + TimeDelta kMaxPacketDuration = kRtt * RtpPacketHistory::kMinPacketDurationRtt * RtpPacketHistory::kPacketCullingDelayFactor; - fake_clock_.AdvanceTimeMilliseconds(kMaxPacketDurationMs - 1); + fake_clock_.AdvanceTime(kMaxPacketDuration - TimeDelta::Millis(1)); // First packet should still be there. EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); @@ -460,122 +359,80 @@ TEST_P(RtpPacketHistoryTest, RemovesOldWithCullingHighRtt) { // Advance to where packet can be culled, even if buffer is not full. fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + 1)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, CullWithAcks) { - const int64_t kPacketLifetime = RtpPacketHistory::kMinPacketDurationMs * - RtpPacketHistory::kPacketCullingDelayFactor; + const TimeDelta kPacketLifetime = RtpPacketHistory::kMinPacketDuration * + RtpPacketHistory::kPacketCullingDelayFactor; - const int64_t start_time = fake_clock_.TimeInMilliseconds(); + const Timestamp start_time = fake_clock_.CurrentTime(); hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 10); // Insert three packets 33ms apart, immediately mark them as sent. std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(kStartSeqNum); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); packet = CreateRtpPacket(To16u(kStartSeqNum + 1)); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + 1)); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); packet = CreateRtpPacket(To16u(kStartSeqNum + 2)); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(To16u(kStartSeqNum + 2)); + hist_.PutRtpPacket(std::move(packet), + /*send_time=*/fake_clock_.CurrentTime()); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Remove middle one using ack, check that only that one is gone. std::vector acked_sequence_numbers = {To16u(kStartSeqNum + 1)}; hist_.CullAcknowledgedPackets(acked_sequence_numbers); - EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Advance time to where second packet would have expired, verify first packet // is removed. - int64_t second_packet_expiry_time = start_time + kPacketLifetime + 33 + 1; - fake_clock_.AdvanceTimeMilliseconds(second_packet_expiry_time - - fake_clock_.TimeInMilliseconds()); - hist_.SetRtt(1); // Trigger culling of old packets. - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); + Timestamp second_packet_expiry_time = + start_time + kPacketLifetime + TimeDelta::Millis(33 + 1); + fake_clock_.AdvanceTime(second_packet_expiry_time - + fake_clock_.CurrentTime()); + hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_TRUE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); // Advance to where last packet expires, verify all gone. fake_clock_.AdvanceTimeMilliseconds(33); - hist_.SetRtt(1); // Trigger culling of old packets. - EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1)).has_value()); - EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 2)).has_value()); -} - -TEST_P(RtpPacketHistoryTest, SetsPendingTransmissionState) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); - - // Set size to remove old packets as soon as possible. - hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - - // Add a packet, without send time, indicating it's in pacer queue. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - /* send_time_ms = */ absl::nullopt); - - // Packet is pending transmission. - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); - - // Packet sent, state should be back to non-pending. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_FALSE(packet_state->pending_transmission); - - // Time for a retransmission. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); - EXPECT_TRUE(hist_.SetPendingTransmission(kStartSeqNum)); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); - - // Packet sent. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - // Too early for retransmission. - ASSERT_FALSE(hist_.GetPacketState(kStartSeqNum).has_value()); - - // Retransmission allowed again, it's not in a pending state. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_FALSE(packet_state->pending_transmission); + hist_.SetRtt(TimeDelta::Millis(1)); // Trigger culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 1))); + EXPECT_FALSE(hist_.GetPacketState(To16u(kStartSeqNum + 2))); } TEST_P(RtpPacketHistoryTest, GetPacketAndSetSent) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + hist_.SetRtt(kRtt); // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add a sent packet to the history. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Retransmission request, first retransmission is allowed immediately. EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Packet not yet sent, new retransmission not allowed. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); + fake_clock_.AdvanceTime(kRtt); EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Mark as sent, but too early for retransmission. @@ -583,14 +440,14 @@ TEST_P(RtpPacketHistoryTest, GetPacketAndSetSent) { EXPECT_FALSE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Enough time has passed, retransmission is allowed again. - fake_clock_.AdvanceTimeMilliseconds(kRttMs); + fake_clock_.AdvanceTime(kRtt); EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { const uint32_t kSsrc = 92384762; - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - hist_.SetRtt(kRttMs); + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + hist_.SetRtt(kRtt); // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); @@ -598,7 +455,7 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { // Add a sent packet to the history, with a set SSRC. std::unique_ptr packet = CreateRtpPacket(kStartSeqNum); packet->SetSsrc(kSsrc); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); // Retransmission request, simulate an RTX-like encapsulation, were the packet // is sent on a different SSRC. @@ -617,8 +474,7 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulation) { TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulationAbortOnNullptr) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMicroseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Retransmission request, but the encapsulator determines that this packet is // not suitable for retransmission (bandwidth exhausted?) so the retransmit is @@ -632,36 +488,31 @@ TEST_P(RtpPacketHistoryTest, GetPacketWithEncapsulationAbortOnNullptr) { } TEST_P(RtpPacketHistoryTest, DontRemovePendingTransmissions) { - const int64_t kRttMs = RtpPacketHistory::kMinPacketDurationMs * 2; - const int64_t kPacketTimeoutMs = - kRttMs * RtpPacketHistory::kMinPacketDurationRtt; + const TimeDelta kRtt = RtpPacketHistory::kMinPacketDuration * 2; + const TimeDelta kPacketTimeout = + kRtt * RtpPacketHistory::kMinPacketDurationRtt; // Set size to remove old packets as soon as possible. hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.SetRtt(kRttMs); + hist_.SetRtt(kRtt); // Add a sent packet. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); // Advance clock to just before packet timeout. - fake_clock_.AdvanceTimeMilliseconds(kPacketTimeoutMs - 1); + fake_clock_.AdvanceTime(kPacketTimeout - TimeDelta::Millis(1)); // Mark as enqueued in pacer. - EXPECT_TRUE(hist_.SetPendingTransmission(kStartSeqNum)); + EXPECT_TRUE(hist_.GetPacketAndMarkAsPending(kStartSeqNum)); // Advance clock to where packet would have timed out. It should still // be there and pending. fake_clock_.AdvanceTimeMilliseconds(1); - absl::optional packet_state = - hist_.GetPacketState(kStartSeqNum); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_TRUE(packet_state->pending_transmission); + EXPECT_TRUE(hist_.GetPacketState(kStartSeqNum)); // Packet sent. Now it can be removed. - EXPECT_TRUE(hist_.GetPacketAndSetSendTime(kStartSeqNum)); - hist_.SetRtt(kRttMs); // Force culling of old packets. - packet_state = hist_.GetPacketState(kStartSeqNum); - ASSERT_FALSE(packet_state.has_value()); + hist_.MarkPacketAsSent(kStartSeqNum); + hist_.SetRtt(kRtt); // Force culling of old packets. + EXPECT_FALSE(hist_.GetPacketState(kStartSeqNum)); } TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { @@ -673,12 +524,11 @@ TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); // Add two sent packets, one millisecond apart. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); // Latest packet given equal retransmission count. @@ -709,26 +559,24 @@ TEST_P(RtpPacketHistoryTest, PrioritizedPayloadPadding) { TEST_P(RtpPacketHistoryTest, NoPendingPacketAsPadding) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); // If packet is pending retransmission, don't try to use it as padding. - hist_.SetPendingTransmission(kStartSeqNum); + hist_.GetPacketAndMarkAsPending(kStartSeqNum); EXPECT_EQ(nullptr, hist_.GetPayloadPaddingPacket()); // Market it as no longer pending, should be usable as padding again. - hist_.GetPacketAndSetSendTime(kStartSeqNum); + hist_.MarkPacketAsSent(kStartSeqNum); EXPECT_EQ(hist_.GetPayloadPaddingPacket()->SequenceNumber(), kStartSeqNum); } TEST_P(RtpPacketHistoryTest, PayloadPaddingWithEncapsulation) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 1); - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(1); // Aborted padding. @@ -749,10 +597,9 @@ TEST_P(RtpPacketHistoryTest, PayloadPaddingWithEncapsulation) { TEST_P(RtpPacketHistoryTest, NackAfterAckIsNoop) { hist_.SetStorePacketsStatus(StorageMode::kStoreAndCull, 2); // Add two sent packets. - hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), - fake_clock_.TimeInMilliseconds()); + hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum), fake_clock_.CurrentTime()); hist_.PutRtpPacket(CreateRtpPacket(kStartSeqNum + 1), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); // Remove newest one. hist_.CullAcknowledgedPackets(std::vector{kStartSeqNum + 1}); // Retransmission request for already acked packet, should be noop. @@ -766,29 +613,22 @@ TEST_P(RtpPacketHistoryTest, OutOfOrderInsertRemoval) { // Insert packets, out of order, including both forwards and backwards // sequence number wraps. const int seq_offsets[] = {0, 1, -1, 2, -2, 3, -3}; - const int64_t start_time_ms = fake_clock_.TimeInMilliseconds(); for (int offset : seq_offsets) { uint16_t seq_no = To16u(kStartSeqNum + offset); std::unique_ptr packet = CreateRtpPacket(seq_no); packet->SetPayloadSize(50); - hist_.PutRtpPacket(std::move(packet), fake_clock_.TimeInMilliseconds()); - hist_.GetPacketAndSetSendTime(seq_no); + hist_.PutRtpPacket(std::move(packet), fake_clock_.CurrentTime()); fake_clock_.AdvanceTimeMilliseconds(33); } // Check packet are there and remove them in the same out-of-order fashion. - int64_t expected_time_offset_ms = 0; for (int offset : seq_offsets) { uint16_t seq_no = To16u(kStartSeqNum + offset); - absl::optional packet_state = - hist_.GetPacketState(seq_no); - ASSERT_TRUE(packet_state.has_value()); - EXPECT_EQ(packet_state->send_time_ms, - start_time_ms + expected_time_offset_ms); + EXPECT_TRUE(hist_.GetPacketState(seq_no)); std::vector acked_sequence_numbers = {seq_no}; hist_.CullAcknowledgedPackets(acked_sequence_numbers); - expected_time_offset_ms += 33; + EXPECT_FALSE(hist_.GetPacketState(seq_no)); } } @@ -805,7 +645,7 @@ TEST_P(RtpPacketHistoryTest, UsesLastPacketAsPaddingWithPrioOff) { for (size_t i = 0; i < kHistorySize; ++i) { hist_.PutRtpPacket(CreateRtpPacket(To16u(kStartSeqNum + i)), - fake_clock_.TimeInMilliseconds()); + fake_clock_.CurrentTime()); hist_.MarkPacketAsSent(To16u(kStartSeqNum + i)); fake_clock_.AdvanceTimeMilliseconds(1); diff --git a/modules/rtp_rtcp/source/rtp_packet_received.h b/modules/rtp_rtcp/source/rtp_packet_received.h index 431d3f52be..f290a643a4 100644 --- a/modules/rtp_rtcp/source/rtp_packet_received.h +++ b/modules/rtp_rtcp/source/rtp_packet_received.h @@ -14,7 +14,6 @@ #include -#include "absl/base/attributes.h" #include "api/array_view.h" #include "api/ref_counted_base.h" #include "api/rtp_headers.h" @@ -49,15 +48,6 @@ class RtpPacketReceived : public RtpPacket { webrtc::Timestamp arrival_time() const { return arrival_time_; } void set_arrival_time(webrtc::Timestamp time) { arrival_time_ = time; } - ABSL_DEPRECATED("Use arrival_time() instead") - int64_t arrival_time_ms() const { - return arrival_time_.IsMinusInfinity() ? -1 : arrival_time_.ms(); - } - ABSL_DEPRECATED("Use set_arrival_time() instead") - void set_arrival_time_ms(int64_t time) { - arrival_time_ = webrtc::Timestamp::Millis(time); - } - // Flag if packet was recovered via RTX or FEC. bool recovered() const { return recovered_; } void set_recovered(bool value) { recovered_ = value; } diff --git a/modules/rtp_rtcp/source/rtp_packet_to_send.h b/modules/rtp_rtcp/source/rtp_packet_to_send.h index 12341ef6cf..8c0fc7bd5c 100644 --- a/modules/rtp_rtcp/source/rtp_packet_to_send.h +++ b/modules/rtp_rtcp/source/rtp_packet_to_send.h @@ -19,6 +19,7 @@ #include "api/array_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" +#include "api/units/timestamp.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" @@ -44,9 +45,8 @@ class RtpPacketToSend : public RtpPacket { ~RtpPacketToSend(); // Time in local time base as close as it can to frame capture time. - int64_t capture_time_ms() const { return capture_time_ms_; } - - void set_capture_time_ms(int64_t time) { capture_time_ms_ = time; } + webrtc::Timestamp capture_time() const { return capture_time_; } + void set_capture_time(webrtc::Timestamp time) { capture_time_ = time; } void set_packet_type(RtpPacketMediaType type) { packet_type_ = type; } absl::optional packet_type() const { @@ -77,27 +77,27 @@ class RtpPacketToSend : public RtpPacket { additional_data_ = std::move(data); } - void set_packetization_finish_time_ms(int64_t time) { + void set_packetization_finish_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kPacketizationFinishDeltaOffset); } - void set_pacer_exit_time_ms(int64_t time) { + void set_pacer_exit_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kPacerExitDeltaOffset); } - void set_network_time_ms(int64_t time) { + void set_network_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kNetworkTimestampDeltaOffset); } - void set_network2_time_ms(int64_t time) { + void set_network2_time(webrtc::Timestamp time) { SetExtension( - VideoSendTiming::GetDeltaCappedMs(capture_time_ms_, time), + VideoSendTiming::GetDeltaCappedMs(time - capture_time_), VideoTimingExtension::kNetwork2TimestampDeltaOffset); } @@ -121,7 +121,7 @@ class RtpPacketToSend : public RtpPacket { bool is_red() const { return is_red_; } private: - int64_t capture_time_ms_ = 0; + webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero(); absl::optional packet_type_; bool allow_retransmission_ = false; absl::optional retransmitted_sequence_number_; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 367785846b..a98dab3972 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -787,7 +787,7 @@ void ModuleRtpRtcpImpl::set_rtt_ms(int64_t rtt_ms) { rtt_ms_ = rtt_ms; } if (rtp_sender_) { - rtp_sender_->packet_history.SetRtt(rtt_ms); + rtp_sender_->packet_history.SetRtt(TimeDelta::Millis(rtt_ms)); } } diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc index 6f39c5ce53..06fbbe01b5 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2.cc @@ -745,7 +745,7 @@ void ModuleRtpRtcpImpl2::set_rtt_ms(int64_t rtt_ms) { rtt_ms_ = rtt_ms; } if (rtp_sender_) { - rtp_sender_->packet_history.SetRtt(rtt_ms); + rtp_sender_->packet_history.SetRtt(TimeDelta::Millis(rtt_ms)); } } diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc index 8592e42173..48b0aac037 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl2_unittest.cc @@ -157,7 +157,7 @@ struct TestConfig { bool with_overhead = false; }; -class FieldTrialConfig : public WebRtcKeyValueConfig { +class FieldTrialConfig : public FieldTrialsView { public: static FieldTrialConfig GetFromTestConfig(const TestConfig& config) { FieldTrialConfig trials; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_interface.h b/modules/rtp_rtcp/source/rtp_rtcp_interface.h index a411b237a0..48c6071d81 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_interface.h +++ b/modules/rtp_rtcp/source/rtp_rtcp_interface.h @@ -16,9 +16,9 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/frame_transformer_interface.h" #include "api/scoped_refptr.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/video/video_bitrate_allocation.h" #include "modules/rtp_rtcp/include/receive_statistics.h" #include "modules/rtp_rtcp/include/report_block_data.h" @@ -128,7 +128,7 @@ class RtpRtcpInterface : public RtcpFeedbackSenderInterface { // If set, field trials are read from `field_trials`, otherwise // defaults to webrtc::FieldTrialBasedConfig. - const WebRtcKeyValueConfig* field_trials = nullptr; + const FieldTrialsView* field_trials = nullptr; // SSRCs for media and retransmission, respectively. // FlexFec SSRC is fetched from `flexfec_sender`. diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index c3321d8723..1c3cb286dd 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -138,7 +138,7 @@ bool HasBweExtension(const RtpHeaderExtensionMap& extensions_map) { extensions_map.IsRegistered(kRtpExtensionTransmissionTimeOffset); } -double GetMaxPaddingSizeFactor(const WebRtcKeyValueConfig* field_trials) { +double GetMaxPaddingSizeFactor(const FieldTrialsView* field_trials) { // Too low factor means RTX payload padding is rarely used and ineffective. // Too high means we risk interrupting regular media packets. // In practice, 3x seems to yield reasonable results. @@ -155,12 +155,6 @@ double GetMaxPaddingSizeFactor(const WebRtcKeyValueConfig* field_trials) { } // namespace -RTPSender::RTPSender(const RtpRtcpInterface::Configuration& config, - RtpPacketHistory* packet_history, - RtpPacketSender* packet_sender, - PacketSequencer*) - : RTPSender(config, packet_history, packet_sender) {} - RTPSender::RTPSender(const RtpRtcpInterface::Configuration& config, RtpPacketHistory* packet_history, RtpPacketSender* packet_sender) @@ -286,16 +280,7 @@ void RTPSender::SetRtxPayloadType(int payload_type, } int32_t RTPSender::ReSendPacket(uint16_t packet_id) { - // Try to find packet in RTP packet history. Also verify RTT here, so that we - // don't retransmit too often. - absl::optional stored_packet = - packet_history_->GetPacketState(packet_id); - if (!stored_packet || stored_packet->pending_transmission) { - // Packet not found or already queued for retransmission, ignore. - return 0; - } - - const int32_t packet_size = static_cast(stored_packet->packet_size); + int32_t packet_size = 0; const bool rtx = (RtxStatus() & kRtxRetransmitted) > 0; std::unique_ptr packet = @@ -304,6 +289,7 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id) { // Check if we're overusing retransmission bitrate. // TODO(sprang): Add histograms for nack success or failure // reasons. + packet_size = stored_packet.size(); std::unique_ptr retransmit_packet; if (retransmission_rate_limiter_ && !retransmission_rate_limiter_->TryUseRate(packet_size)) { @@ -321,7 +307,14 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id) { } return retransmit_packet; }); + if (packet_size == 0) { + // Packet not found or already queued for retransmission, ignore. + RTC_DCHECK(!packet); + return 0; + } if (!packet) { + // Packet was found, but lambda helper above chose not to create + // `retransmit_packet` out of it. return -1; } packet->set_packet_type(RtpPacketMediaType::kRetransmission); @@ -355,7 +348,7 @@ void RTPSender::OnReceivedAckOnRtxSsrc( void RTPSender::OnReceivedNack( const std::vector& nack_sequence_numbers, int64_t avg_rtt) { - packet_history_->SetRtt(5 + avg_rtt); + packet_history_->SetRtt(TimeDelta::Millis(5 + avg_rtt)); for (uint16_t seq_no : nack_sequence_numbers) { const int32_t bytes_sent = ReSendPacket(seq_no); if (bytes_sent < 0) { @@ -484,13 +477,11 @@ std::vector> RTPSender::GeneratePadding( bool RTPSender::SendToNetwork(std::unique_ptr packet) { RTC_DCHECK(packet); - int64_t now_ms = clock_->TimeInMilliseconds(); - auto packet_type = packet->packet_type(); RTC_CHECK(packet_type) << "Packet type must be set before sending."; - if (packet->capture_time_ms() <= 0) { - packet->set_capture_time_ms(now_ms); + if (packet->capture_time() <= Timestamp::Zero()) { + packet->set_capture_time(clock_->CurrentTime()); } std::vector> packets; @@ -503,13 +494,13 @@ bool RTPSender::SendToNetwork(std::unique_ptr packet) { void RTPSender::EnqueuePackets( std::vector> packets) { RTC_DCHECK(!packets.empty()); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); for (auto& packet : packets) { RTC_DCHECK(packet); RTC_CHECK(packet->packet_type().has_value()) << "Packet type must be set before sending."; - if (packet->capture_time_ms() <= 0) { - packet->set_capture_time_ms(now_ms); + if (packet->capture_time() <= Timestamp::Zero()) { + packet->set_capture_time(now); } } @@ -726,7 +717,7 @@ std::unique_ptr RTPSender::BuildRtxPacket( 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()); + rtx_packet->set_capture_time(packet.capture_time()); return rtx_packet; } diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index d892970542..cb2c99e122 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -21,12 +21,11 @@ #include "absl/types/optional.h" #include "api/array_view.h" #include "api/call/transport.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "modules/rtp_rtcp/include/flexfec_sender.h" #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" @@ -47,14 +46,6 @@ class RTPSender { RTPSender(const RtpRtcpInterface::Configuration& config, RtpPacketHistory* packet_history, RtpPacketSender* packet_sender); - - ABSL_DEPRECATED("bugs.webrtc.org/11340") - RTPSender(const RtpRtcpInterface::Configuration& config, - RtpPacketHistory* packet_history, - RtpPacketSender* packet_sender, - PacketSequencer* packet_sequencer); - - RTPSender() = delete; RTPSender(const RTPSender&) = delete; RTPSender& operator=(const RTPSender&) = delete; diff --git a/modules/rtp_rtcp/source/rtp_sender_audio.cc b/modules/rtp_rtcp/source/rtp_sender_audio.cc index 207d1ca045..c0a8075306 100644 --- a/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ b/modules/rtp_rtcp/source/rtp_sender_audio.cc @@ -272,7 +272,7 @@ bool RTPSenderAudio::SendAudio(AudioFrameType frame_type, packet->SetMarker(MarkerBit(frame_type, payload_type)); packet->SetPayloadType(payload_type); packet->SetTimestamp(rtp_timestamp); - packet->set_capture_time_ms(clock_->TimeInMilliseconds()); + packet->set_capture_time(clock_->CurrentTime()); // Update audio level extension, if included. packet->SetExtension( frame_type == AudioFrameType::kAudioFrameSpeech, audio_level_dbov); @@ -370,7 +370,7 @@ bool RTPSenderAudio::SendTelephoneEventPacket(bool ended, packet->SetMarker(marker_bit); packet->SetSsrc(rtp_sender_->SSRC()); packet->SetTimestamp(dtmf_timestamp); - packet->set_capture_time_ms(clock_->TimeInMilliseconds()); + packet->set_capture_time(clock_->CurrentTime()); // Create DTMF data. uint8_t* dtmfbuffer = packet->AllocatePayload(kDtmfSize); diff --git a/modules/rtp_rtcp/source/rtp_sender_egress.cc b/modules/rtp_rtcp/source/rtp_sender_egress.cc index eb55378083..4a72059beb 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress.cc @@ -30,7 +30,7 @@ constexpr size_t kRtpSequenceNumberMapMaxEntries = 1 << 13; constexpr TimeDelta kUpdateInterval = TimeDelta::Millis(kBitrateStatisticsWindowMs); -bool IsTrialSetTo(const WebRtcKeyValueConfig* field_trials, +bool IsTrialSetTo(const FieldTrialsView* field_trials, absl::string_view name, absl::string_view value) { FieldTrialBasedConfig default_trials; @@ -225,7 +225,7 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, // In case of VideoTimingExtension, since it's present not in every packet, // data after rtp header may be corrupted if these packets are protected by // the FEC. - int64_t diff_ms = now_ms - packet->capture_time_ms(); + int64_t diff_ms = now_ms - packet->capture_time().ms(); if (packet->HasExtension()) { packet->SetExtension(kTimestampTicksPerMs * diff_ms); } @@ -236,9 +236,9 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, if (packet->HasExtension()) { if (populate_network2_timestamp_) { - packet->set_network2_time_ms(now_ms); + packet->set_network2_time(Timestamp::Millis(now_ms)); } else { - packet->set_pacer_exit_time_ms(now_ms); + packet->set_pacer_exit_time(Timestamp::Millis(now_ms)); } } @@ -265,8 +265,8 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, if (packet->packet_type() != RtpPacketMediaType::kPadding && packet->packet_type() != RtpPacketMediaType::kRetransmission) { - UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc); - UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(), + UpdateDelayStatistics(packet->capture_time().ms(), now_ms, packet_ssrc); + UpdateOnSendPacket(options.packet_id, packet->capture_time().ms(), packet_ssrc); } @@ -276,7 +276,7 @@ void RtpSenderEgress::SendPacket(RtpPacketToSend* packet, // actual sending fails. if (is_media && packet->allow_retransmission()) { packet_history_->PutRtpPacket(std::make_unique(*packet), - now_ms); + Timestamp::Millis(now_ms)); } else if (packet->retransmitted_sequence_number()) { packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number()); } diff --git a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc index 0f2a218ae4..33c06c4493 100644 --- a/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_egress_unittest.cc @@ -87,7 +87,7 @@ class MockSendSideDelayObserver : public SendSideDelayObserver { (override)); }; -class FieldTrialConfig : public WebRtcKeyValueConfig { +class FieldTrialConfig : public FieldTrialsView { public: FieldTrialConfig() : overhead_enabled_(false) {} ~FieldTrialConfig() override {} @@ -184,7 +184,7 @@ class RtpSenderEgressTest : public ::testing::TestWithParam { packet->set_packet_type(RtpPacketMediaType::kVideo); packet->SetMarker(marker_bit); packet->SetTimestamp(capture_time_ms * 90); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); packet->SetSequenceNumber(sequence_number_++); return packet; } @@ -496,8 +496,7 @@ TEST_P(RtpSenderEgressTest, DoesNotPutNotRetransmittablePacketsInHistory) { std::unique_ptr packet = BuildRtpPacket(); packet->set_allow_retransmission(false); sender->SendPacket(packet.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(packet->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, PutsRetransmittablePacketsInHistory) { @@ -508,10 +507,7 @@ TEST_P(RtpSenderEgressTest, PutsRetransmittablePacketsInHistory) { std::unique_ptr packet = BuildRtpPacket(); packet->set_allow_retransmission(true); sender->SendPacket(packet.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, DoesNotPutNonMediaInHistory) { @@ -527,22 +523,20 @@ TEST_P(RtpSenderEgressTest, DoesNotPutNonMediaInHistory) { retransmission->set_retransmitted_sequence_number( retransmission->SequenceNumber()); sender->SendPacket(retransmission.get(), PacedPacketInfo()); - EXPECT_FALSE(packet_history_.GetPacketState(retransmission->SequenceNumber()) - .has_value()); + EXPECT_FALSE( + packet_history_.GetPacketState(retransmission->SequenceNumber())); std::unique_ptr fec = BuildRtpPacket(); fec->set_allow_retransmission(true); fec->set_packet_type(RtpPacketMediaType::kForwardErrorCorrection); sender->SendPacket(fec.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(fec->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(fec->SequenceNumber())); std::unique_ptr padding = BuildRtpPacket(); padding->set_allow_retransmission(true); padding->set_packet_type(RtpPacketMediaType::kPadding); sender->SendPacket(padding.get(), PacedPacketInfo()); - EXPECT_FALSE( - packet_history_.GetPacketState(padding->SequenceNumber()).has_value()); + EXPECT_FALSE(packet_history_.GetPacketState(padding->SequenceNumber())); } TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { @@ -554,10 +548,7 @@ TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { std::unique_ptr media_packet = BuildRtpPacket(); media_packet->set_allow_retransmission(true); sender->SendPacket(media_packet.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); // Simulate a retransmission, marking the packet as pending. std::unique_ptr retransmission = @@ -565,16 +556,11 @@ TEST_P(RtpSenderEgressTest, UpdatesSendStatusOfRetransmittedPackets) { retransmission->set_retransmitted_sequence_number( media_packet->SequenceNumber()); retransmission->set_packet_type(RtpPacketMediaType::kRetransmission); - EXPECT_THAT(packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional(Field( - &RtpPacketHistory::PacketState::pending_transmission, true))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); // Simulate packet leaving pacer, the packet should be marked as non-pending. sender->SendPacket(retransmission.get(), PacedPacketInfo()); - EXPECT_THAT( - packet_history_.GetPacketState(media_packet->SequenceNumber()), - Optional( - Field(&RtpPacketHistory::PacketState::pending_transmission, false))); + EXPECT_TRUE(packet_history_.GetPacketState(media_packet->SequenceNumber())); } TEST_P(RtpSenderEgressTest, StreamDataCountersCallbacks) { @@ -756,7 +742,7 @@ TEST_P(RtpSenderEgressTest, SendPacketUpdatesExtensions) { std::unique_ptr sender = CreateRtpSenderEgress(); std::unique_ptr packet = BuildRtpPacket(); - packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds()); + packet->set_packetization_finish_time(clock_->CurrentTime()); const int32_t kDiffMs = 10; time_controller_.AdvanceTime(TimeDelta::Millis(kDiffMs)); diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 99c2c8087a..6eba47741c 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -100,7 +100,7 @@ class MockRtpPacketPacer : public RtpPacketSender { (override)); }; -class FieldTrialConfig : public WebRtcKeyValueConfig { +class FieldTrialConfig : public FieldTrialsView { public: FieldTrialConfig() : max_padding_factor_(1200) {} ~FieldTrialConfig() override {} @@ -170,7 +170,7 @@ class RtpSenderTest : public ::testing::Test { /*require_marker_before_media_padding=*/!config.audio, clock_); rtp_sender_ = std::make_unique(config, packet_history_.get(), - config.paced_sender, nullptr); + config.paced_sender); sequencer_->set_media_sequence_number(kSeqNum); rtp_sender_->SetTimestampOffset(0); } @@ -198,7 +198,7 @@ class RtpSenderTest : public ::testing::Test { packet->set_packet_type(RtpPacketMediaType::kVideo); packet->SetMarker(marker_bit); packet->SetTimestamp(timestamp); - packet->set_capture_time_ms(capture_time_ms); + packet->set_capture_time(Timestamp::Millis(capture_time_ms)); return packet; } @@ -354,13 +354,12 @@ TEST_F(RtpSenderTest, PaddingAlwaysAllowedOnAudio) { TEST_F(RtpSenderTest, SendToNetworkForwardsPacketsToPacer) { auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, 0); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); - EXPECT_CALL( - mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms)))))); + EXPECT_CALL(mock_paced_sender_, + EnqueuePackets(ElementsAre(AllOf( + Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), + Pointee(Property(&RtpPacketToSend::capture_time, now)))))); EXPECT_TRUE( rtp_sender_->SendToNetwork(std::make_unique(*packet))); } @@ -372,13 +371,14 @@ TEST_F(RtpSenderTest, ReSendPacketForwardsPacketsToPacer) { auto packet = BuildRtpPacket(kPayload, kMarkerBit, kTimestamp, now_ms); packet->SetSequenceNumber(kSeqNum); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), now_ms); + packet_history_->PutRtpPacket(std::move(packet), Timestamp::Millis(now_ms)); EXPECT_CALL(mock_paced_sender_, EnqueuePackets(ElementsAre(AllOf( Pointee(Property(&RtpPacketToSend::Ssrc, kSsrc)), Pointee(Property(&RtpPacketToSend::SequenceNumber, kSeqNum)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, now_ms)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(now_ms))), Pointee(Property(&RtpPacketToSend::packet_type, RtpPacketMediaType::kRetransmission)))))); EXPECT_TRUE(rtp_sender_->ReSendPacket(kSeqNum)); @@ -527,10 +527,11 @@ TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) { // Start by sending one media packet. EXPECT_CALL( mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time)))))); + EnqueuePackets(ElementsAre( + AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time))))))); std::unique_ptr media_packet = SendPacket(start_time, /*payload_size=*/600); sequencer_->Sequence(*media_packet); @@ -546,8 +547,8 @@ TEST_F(RtpSenderTest, UpdatesTimestampsOnPlainRtxPadding) { Property(&RtpPacketToSend::padding_size, kMaxPaddingLength), Property(&RtpPacketToSend::Timestamp, start_timestamp + (kTimestampTicksPerMs * kTimeDiff.ms())), - Property(&RtpPacketToSend::capture_time_ms, - start_time + kTimeDiff.ms()))))); + Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time) + kTimeDiff))))); } TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) { @@ -563,27 +564,29 @@ TEST_F(RtpSenderTest, KeepsTimestampsOnPayloadPadding) { // Start by sending one media packet and putting in the packet history. EXPECT_CALL( mock_paced_sender_, - EnqueuePackets(ElementsAre(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time)))))); + EnqueuePackets(ElementsAre( + AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time))))))); std::unique_ptr media_packet = SendPacket(start_time, kPayloadSize); - packet_history_->PutRtpPacket(std::move(media_packet), start_time); + packet_history_->PutRtpPacket(std::move(media_packet), + Timestamp::Millis(start_time)); // Advance time before sending padding. const TimeDelta kTimeDiff = TimeDelta::Millis(17); time_controller_.AdvanceTime(kTimeDiff); // Timestamps on payload padding should be set to original. - EXPECT_THAT( - GeneratePadding(/*target_size_bytes=*/100), - Each(AllOf( - Pointee(Property(&RtpPacketToSend::padding_size, 0u)), - Pointee(Property(&RtpPacketToSend::payload_size, - kPayloadSize + kRtxHeaderSize)), - Pointee(Property(&RtpPacketToSend::Timestamp, start_timestamp)), - Pointee(Property(&RtpPacketToSend::capture_time_ms, start_time))))); + EXPECT_THAT(GeneratePadding(/*target_size_bytes=*/100), + Each(AllOf(Pointee(Property(&RtpPacketToSend::padding_size, 0u)), + Pointee(Property(&RtpPacketToSend::payload_size, + kPayloadSize + kRtxHeaderSize)), + Pointee(Property(&RtpPacketToSend::Timestamp, + start_timestamp)), + Pointee(Property(&RtpPacketToSend::capture_time, + Timestamp::Millis(start_time)))))); } // Test that the MID header extension is included on sent packets when @@ -624,7 +627,7 @@ TEST_F(RtpSenderTest, RidIncludedOnRtxSentPackets) { .WillOnce([&](std::vector> packets) { sequencer_->Sequence(*packets[0]); packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); SendGenericPacket(); @@ -703,7 +706,7 @@ TEST_F(RtpSenderTest, MidAndRidIncludedOnFirstRtxPacket) { EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) .WillOnce([&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto second_built_packet = SendGenericPacket(); @@ -732,7 +735,7 @@ TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) { sequencer_->Sequence(*first_built_packet); packet_history_->PutRtpPacket( std::make_unique(*first_built_packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); rtp_sender_->OnReceivedAckOnSsrc(first_built_packet->SequenceNumber()); // The second packet will include neither since an ack was received. @@ -740,7 +743,7 @@ TEST_F(RtpSenderTest, MidAndRidNotIncludedOnRtxPacketsAfterAck) { sequencer_->Sequence(*second_built_packet); packet_history_->PutRtpPacket( std::make_unique(*second_built_packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); // The first RTX packet will include MID and RRID. EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) @@ -780,7 +783,7 @@ TEST_F(RtpSenderTest, MidAndRidAlwaysIncludedOnRtxPacketsWhenConfigured) { .WillRepeatedly( [&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto media_packet1 = SendGenericPacket(); rtp_sender_->OnReceivedAckOnSsrc(media_packet1->SequenceNumber()); @@ -848,7 +851,7 @@ TEST_F(RtpSenderTest, MidAndRridNotIncludedOnRtxPacketsAfterRtpStateRestored) { EXPECT_CALL(mock_paced_sender_, EnqueuePackets(SizeIs(1))) .WillOnce([&](std::vector> packets) { packet_history_->PutRtpPacket(std::move(packets[0]), - clock_->TimeInMilliseconds()); + clock_->CurrentTime()); }); auto built_packet = SendGenericPacket(); @@ -875,7 +878,7 @@ TEST_F(RtpSenderTest, RespectsNackBitrateLimit) { sequencer_->Sequence(*packet); sequence_numbers.push_back(packet->SequenceNumber()); packet_history_->PutRtpPacket(std::move(packet), - /*send_time=*/clock_->TimeInMilliseconds()); + /*send_time=*/clock_->CurrentTime()); time_controller_.AdvanceTime(TimeDelta::Millis(1)); } @@ -992,8 +995,7 @@ TEST_F(RtpSenderTest, SendPacketHandlesRetransmissionHistory) { BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds()); const uint16_t media_sequence_number = packet->SequenceNumber(); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Simulate successful retransmission request. time_controller_.AdvanceTime(TimeDelta::Millis(30)); @@ -1020,8 +1022,7 @@ TEST_F(RtpSenderTest, MarksRetransmittedPackets) { BuildRtpPacket(kPayload, true, 0, clock_->TimeInMilliseconds()); const uint16_t media_sequence_number = packet->SequenceNumber(); packet->set_allow_retransmission(true); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Expect a retransmission packet marked with which packet it is a // retransmit of. @@ -1053,8 +1054,7 @@ TEST_F(RtpSenderTest, GeneratedPaddingHasBweExtensions) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kMinPaddingSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Generate a plain padding packet, check that extensions are registered. std::vector> generated_packets = @@ -1096,8 +1096,7 @@ TEST_F(RtpSenderTest, GeneratePaddingResendsOldPacketsWithRtx) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Generated padding has large enough budget that the video packet should be // retransmitted as padding. @@ -1147,8 +1146,7 @@ TEST_F(RtpSenderTest, LimitsPayloadPaddingSize) { packet->set_allow_retransmission(true); packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Smallest target size that will result in the sent packet being returned as // padding. @@ -1189,8 +1187,7 @@ TEST_F(RtpSenderTest, GeneratePaddingCreatesPurePaddingWithoutRtx) { packet->SetPayloadSize(kPayloadPacketSize); packet->set_packet_type(RtpPacketMediaType::kVideo); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), - clock_->TimeInMilliseconds()); + packet_history_->PutRtpPacket(std::move(packet), clock_->CurrentTime()); // Payload padding not available without RTX, only generate plain padding on // the media SSRC. @@ -1267,20 +1264,22 @@ TEST_F(RtpSenderTest, SetsCaptureTimeOnRtxRetransmissions) { /*capture_time_ms=*/start_time_ms); packet->set_allow_retransmission(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Advance time, request an RTX retransmission. Capture timestamp should be // preserved. time_controller_.AdvanceTime(TimeDelta::Millis(10)); - EXPECT_CALL(mock_paced_sender_, - EnqueuePackets(ElementsAre(Pointee(Property( - &RtpPacketToSend::capture_time_ms, start_time_ms))))); + EXPECT_CALL( + mock_paced_sender_, + EnqueuePackets(ElementsAre(Pointee(Property( + &RtpPacketToSend::capture_time, Timestamp::Millis(start_time_ms)))))); EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); } TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) { - const int64_t kRtt = 10; + const TimeDelta kRtt = TimeDelta::Millis(10); EnableRtx(); packet_history_->SetRtt(kRtt); @@ -1292,18 +1291,19 @@ TEST_F(RtpSenderTest, IgnoresNackAfterDisablingMedia) { /*capture_time_ms=*/start_time_ms); packet->set_allow_retransmission(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Disable media sending and try to retransmit the packet, it should fail. rtp_sender_->SetSendingMediaStatus(false); - time_controller_.AdvanceTime(TimeDelta::Millis(kRtt)); + time_controller_.AdvanceTime(kRtt); EXPECT_LT(rtp_sender_->ReSendPacket(kSeqNum), 0); } TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { // Set up retranmission without RTX, so that a plain copy of the old packet is // re-sent instead. - const int64_t kRtt = 10; + const TimeDelta kRtt = TimeDelta::Millis(10); rtp_sender_->SetSendingMediaStatus(true); rtp_sender_->SetRtxStatus(kRtxOff); packet_history_->SetStorePacketsStatus( @@ -1318,7 +1318,8 @@ TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { packet->set_allow_retransmission(true); packet->set_fec_protect_packet(true); sequencer_->Sequence(*packet); - packet_history_->PutRtpPacket(std::move(packet), start_time_ms); + packet_history_->PutRtpPacket(std::move(packet), + Timestamp::Millis(start_time_ms)); // Re-send packet, the retransmitted packet should not have the FEC protection // flag set. @@ -1326,7 +1327,7 @@ TEST_F(RtpSenderTest, DoesntFecProtectRetransmissions) { EnqueuePackets(ElementsAre(Pointee( Property(&RtpPacketToSend::fec_protect_packet, false))))); - time_controller_.AdvanceTime(TimeDelta::Millis(kRtt)); + time_controller_.AdvanceTime(kRtt); EXPECT_GT(rtp_sender_->ReSendPacket(kSeqNum), 0); } diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index da5dad5839..614a3862b0 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -118,7 +118,7 @@ bool IsNoopDelay(const VideoPlayoutDelay& delay) { } absl::optional LoadVideoPlayoutDelayOverride( - const WebRtcKeyValueConfig* key_value_config) { + const FieldTrialsView* key_value_config) { RTC_DCHECK(key_value_config); FieldTrialOptional playout_delay_min_ms("min_ms", absl::nullopt); FieldTrialOptional playout_delay_max_ms("max_ms", absl::nullopt); @@ -534,7 +534,7 @@ bool RTPSenderVideo::SendVideo( RTC_DCHECK_LE(packet_capacity, single_packet->capacity()); single_packet->SetPayloadType(payload_type); single_packet->SetTimestamp(rtp_timestamp); - single_packet->set_capture_time_ms(capture_time_ms); + single_packet->set_capture_time(Timestamp::Millis(capture_time_ms)); // Construct the absolute capture time extension if not provided. if (!video_header.absolute_capture_time.has_value()) { @@ -695,7 +695,7 @@ bool RTPSenderVideo::SendVideo( // Put packetization finish timestamp into extension. if (packet->HasExtension()) { - packet->set_packetization_finish_time_ms(clock_->TimeInMilliseconds()); + packet->set_packetization_finish_time(clock_->CurrentTime()); } packet->set_fec_protect_packet(use_fec); diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index 5164969489..206fcab14f 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -79,7 +79,7 @@ class RTPSenderVideo { bool require_frame_encryption = false; bool enable_retransmit_all_layers = false; absl::optional red_payload_type; - const WebRtcKeyValueConfig* field_trials = nullptr; + const FieldTrialsView* field_trials = nullptr; rtc::scoped_refptr frame_transformer; TaskQueueBase* send_transport_queue = nullptr; }; diff --git a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc index dc845e4d24..c7fc778803 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video_unittest.cc @@ -127,7 +127,7 @@ class TestRtpSenderVideo : public RTPSenderVideo { public: TestRtpSenderVideo(Clock* clock, RTPSender* rtp_sender, - const WebRtcKeyValueConfig& field_trials) + const FieldTrialsView& field_trials) : RTPSenderVideo([&] { Config config; config.clock = clock; @@ -146,7 +146,7 @@ class TestRtpSenderVideo : public RTPSenderVideo { } }; -class FieldTrials : public WebRtcKeyValueConfig { +class FieldTrials : public FieldTrialsView { public: explicit FieldTrials(bool use_send_side_bwe_with_overhead) : use_send_side_bwe_with_overhead_(use_send_side_bwe_with_overhead), @@ -787,7 +787,7 @@ TEST_P(RtpSenderVideoTest, config.clock = &fake_clock_; config.rtp_sender = rtp_module_->RtpSender(); config.field_trials = &field_trials_; - config.frame_encryptor = encryptor; + config.frame_encryptor = encryptor.get(); RTPSenderVideo rtp_sender_video(config); FrameDependencyStructure video_structure; diff --git a/modules/rtp_rtcp/source/time_util.cc b/modules/rtp_rtcp/source/time_util.cc index fe0cfea11f..44ca07dabe 100644 --- a/modules/rtp_rtcp/source/time_util.cc +++ b/modules/rtp_rtcp/source/time_util.cc @@ -18,35 +18,37 @@ namespace webrtc { -uint32_t SaturatedUsToCompactNtp(int64_t us) { +uint32_t SaturatedToCompactNtp(TimeDelta delta) { constexpr uint32_t kMaxCompactNtp = 0xFFFFFFFF; constexpr int kCompactNtpInSecond = 0x10000; - if (us <= 0) + if (delta <= TimeDelta::Zero()) return 0; - if (us >= kMaxCompactNtp * rtc::kNumMicrosecsPerSec / kCompactNtpInSecond) + if (delta.us() >= + kMaxCompactNtp * rtc::kNumMicrosecsPerSec / kCompactNtpInSecond) return kMaxCompactNtp; // To convert to compact ntp need to divide by 1e6 to get seconds, // then multiply by 0x10000 to get the final result. // To avoid float operations, multiplication and division swapped. - return DivideRoundToNearest(us * kCompactNtpInSecond, + return DivideRoundToNearest(delta.us() * kCompactNtpInSecond, rtc::kNumMicrosecsPerSec); } -int64_t CompactNtpRttToMs(uint32_t compact_ntp_interval) { - // Interval to convert expected to be positive, e.g. rtt or delay. +TimeDelta CompactNtpRttToTimeDelta(uint32_t compact_ntp_interval) { + static constexpr TimeDelta kMinRtt = TimeDelta::Millis(1); + // Interval to convert expected to be positive, e.g. RTT or delay. // Because interval can be derived from non-monotonic ntp clock, // it might become negative that is indistinguishable from very large values. - // Since very large rtt/delay are less likely than non-monotonic ntp clock, - // those values consider to be negative and convert to minimum value of 1ms. + // Since very large RTT/delay is less likely than non-monotonic ntp clock, + // such value is considered negative and converted to minimum value of 1ms. if (compact_ntp_interval > 0x80000000) - return 1; + return kMinRtt; // Convert to 64bit value to avoid multiplication overflow. int64_t value = static_cast(compact_ntp_interval); - // To convert to milliseconds need to divide by 2^16 to get seconds, - // then multiply by 1000 to get milliseconds. To avoid float operations, - // multiplication and division swapped. - int64_t ms = DivideRoundToNearest(value * 1000, 1 << 16); - // Rtt value 0 considered too good to be true and increases to 1. - return std::max(ms, 1); + // To convert to TimeDelta need to divide by 2^16 to get seconds, + // then multiply by 1'000'000 to get microseconds. To avoid float operations, + // multiplication and division are swapped. + int64_t us = DivideRoundToNearest(value * rtc::kNumMicrosecsPerSec, 1 << 16); + // Small RTT value is considered too good to be true and increased to 1ms. + return std::max(TimeDelta::Micros(us), kMinRtt); } } // namespace webrtc diff --git a/modules/rtp_rtcp/source/time_util.h b/modules/rtp_rtcp/source/time_util.h index c883e5ca38..9ff444b12e 100644 --- a/modules/rtp_rtcp/source/time_util.h +++ b/modules/rtp_rtcp/source/time_util.h @@ -13,6 +13,8 @@ #include +#include "api/units/time_delta.h" +#include "rtc_base/numerics/safe_conversions.h" #include "system_wrappers/include/ntp_time.h" namespace webrtc { @@ -29,14 +31,26 @@ inline uint32_t CompactNtp(NtpTime ntp) { return (ntp.seconds() << 16) | (ntp.fractions() >> 16); } -// Converts interval in microseconds to compact ntp (1/2^16 seconds) resolution. +// Converts interval to compact ntp (1/2^16 seconds) resolution. // Negative values converted to 0, Overlarge values converted to max uint32_t. -uint32_t SaturatedUsToCompactNtp(int64_t us); +uint32_t SaturatedToCompactNtp(TimeDelta delta); + +// Convert interval to the NTP time resolution (1/2^32 seconds ~= 0.2 ns). +// For deltas with absolute value larger than 35 minutes result is unspecified. +inline constexpr int64_t ToNtpUnits(TimeDelta delta) { + // For better precision `delta` is taken with best TimeDelta precision (us), + // then multiplaction and conversion to seconds are swapped to avoid float + // arithmetic. + // 2^31 us ~= 35.8 minutes. + return (rtc::saturated_cast(delta.us()) * (int64_t{1} << 32)) / + 1'000'000; +} -// Converts interval between compact ntp timestamps to milliseconds. +// Converts interval from compact ntp (1/2^16 seconds) resolution to TimeDelta. // This interval can be up to ~9.1 hours (2^15 seconds). -// Values close to 2^16 seconds consider negative and result in minimum rtt = 1. -int64_t CompactNtpRttToMs(uint32_t compact_ntp_interval); +// Values close to 2^16 seconds are considered negative and are converted to +// minimum value of 1ms. +TimeDelta CompactNtpRttToTimeDelta(uint32_t compact_ntp_interval); } // namespace webrtc #endif // MODULES_RTP_RTCP_SOURCE_TIME_UTIL_H_ diff --git a/modules/rtp_rtcp/source/time_util_unittest.cc b/modules/rtp_rtcp/source/time_util_unittest.cc index 6ff55dda55..b3d557fd83 100644 --- a/modules/rtp_rtcp/source/time_util_unittest.cc +++ b/modules/rtp_rtcp/source/time_util_unittest.cc @@ -9,6 +9,10 @@ */ #include "modules/rtp_rtcp/source/time_util.h" +#include +#include + +#include "api/units/time_delta.h" #include "test/gtest.h" namespace webrtc { @@ -21,18 +25,16 @@ TEST(TimeUtilTest, CompactNtp) { EXPECT_EQ(kNtpMid, CompactNtp(kNtp)); } -TEST(TimeUtilTest, CompactNtpRttToMs) { +TEST(TimeUtilTest, CompactNtpRttToTimeDelta) { const NtpTime ntp1(0x12345, 0x23456); const NtpTime ntp2(0x12654, 0x64335); int64_t ms_diff = ntp2.ToMs() - ntp1.ToMs(); uint32_t ntp_diff = CompactNtp(ntp2) - CompactNtp(ntp1); - int64_t ntp_to_ms_diff = CompactNtpRttToMs(ntp_diff); - - EXPECT_NEAR(ms_diff, ntp_to_ms_diff, 1); + EXPECT_NEAR(CompactNtpRttToTimeDelta(ntp_diff).ms(), ms_diff, 1); } -TEST(TimeUtilTest, CompactNtpRttToMsWithWrap) { +TEST(TimeUtilTest, CompactNtpRttToTimeDeltaWithWrap) { const NtpTime ntp1(0x1ffff, 0x23456); const NtpTime ntp2(0x20000, 0x64335); int64_t ms_diff = ntp2.ToMs() - ntp1.ToMs(); @@ -43,51 +45,84 @@ TEST(TimeUtilTest, CompactNtpRttToMsWithWrap) { ASSERT_LT(CompactNtp(ntp2), CompactNtp(ntp1)); uint32_t ntp_diff = CompactNtp(ntp2) - CompactNtp(ntp1); - int64_t ntp_to_ms_diff = CompactNtpRttToMs(ntp_diff); - - EXPECT_NEAR(ms_diff, ntp_to_ms_diff, 1); + EXPECT_NEAR(CompactNtpRttToTimeDelta(ntp_diff).ms(), ms_diff, 1); } -TEST(TimeUtilTest, CompactNtpRttToMsLarge) { +TEST(TimeUtilTest, CompactNtpRttToTimeDeltaLarge) { const NtpTime ntp1(0x10000, 0x00006); const NtpTime ntp2(0x17fff, 0xffff5); int64_t ms_diff = ntp2.ToMs() - ntp1.ToMs(); // Ntp difference close to 2^15 seconds should convert correctly too. ASSERT_NEAR(ms_diff, ((1 << 15) - 1) * 1000, 1); uint32_t ntp_diff = CompactNtp(ntp2) - CompactNtp(ntp1); - int64_t ntp_to_ms_diff = CompactNtpRttToMs(ntp_diff); - - EXPECT_NEAR(ms_diff, ntp_to_ms_diff, 1); + EXPECT_NEAR(CompactNtpRttToTimeDelta(ntp_diff).ms(), ms_diff, 1); } -TEST(TimeUtilTest, CompactNtpRttToMsNegative) { +TEST(TimeUtilTest, CompactNtpRttToTimeDeltaNegative) { const NtpTime ntp1(0x20000, 0x23456); const NtpTime ntp2(0x1ffff, 0x64335); int64_t ms_diff = ntp2.ToMs() - ntp1.ToMs(); ASSERT_GT(0, ms_diff); // Ntp difference close to 2^16 seconds should be treated as negative. uint32_t ntp_diff = CompactNtp(ntp2) - CompactNtp(ntp1); - int64_t ntp_to_ms_diff = CompactNtpRttToMs(ntp_diff); - EXPECT_EQ(1, ntp_to_ms_diff); + EXPECT_EQ(CompactNtpRttToTimeDelta(ntp_diff), TimeDelta::Millis(1)); } -TEST(TimeUtilTest, SaturatedUsToCompactNtp) { +TEST(TimeUtilTest, SaturatedToCompactNtp) { // Converts negative to zero. - EXPECT_EQ(SaturatedUsToCompactNtp(-1), 0u); - EXPECT_EQ(SaturatedUsToCompactNtp(0), 0u); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Micros(-1)), 0u); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Zero()), 0u); // Converts values just above and just below max uint32_t. - EXPECT_EQ(SaturatedUsToCompactNtp(65536000000), 0xffffffff); - EXPECT_EQ(SaturatedUsToCompactNtp(65535999985), 0xffffffff); - EXPECT_EQ(SaturatedUsToCompactNtp(65535999970), 0xfffffffe); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Micros(65536000000)), 0xffffffff); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Micros(65535999985)), 0xffffffff); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Micros(65535999970)), 0xfffffffe); // Converts half-seconds. - EXPECT_EQ(SaturatedUsToCompactNtp(500000), 0x8000u); - EXPECT_EQ(SaturatedUsToCompactNtp(1000000), 0x10000u); - EXPECT_EQ(SaturatedUsToCompactNtp(1500000), 0x18000u); - // Convert us -> compact_ntp -> ms. Compact ntp precision is ~15us. - EXPECT_EQ(CompactNtpRttToMs(SaturatedUsToCompactNtp(1516)), 2); - EXPECT_EQ(CompactNtpRttToMs(SaturatedUsToCompactNtp(15000)), 15); - EXPECT_EQ(CompactNtpRttToMs(SaturatedUsToCompactNtp(5485)), 5); - EXPECT_EQ(CompactNtpRttToMs(SaturatedUsToCompactNtp(5515)), 6); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Millis(500)), 0x8000u); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Seconds(1)), 0x10000u); + EXPECT_EQ(SaturatedToCompactNtp(TimeDelta::Millis(1'500)), 0x18000u); + // Convert us -> compact_ntp -> TimeDelta. Compact ntp precision is ~15us. + EXPECT_NEAR( + CompactNtpRttToTimeDelta(SaturatedToCompactNtp(TimeDelta::Micros(1'516))) + .us(), + 1'516, 16); + EXPECT_NEAR( + CompactNtpRttToTimeDelta(SaturatedToCompactNtp(TimeDelta::Millis(15))) + .us(), + 15'000, 16); + EXPECT_NEAR( + CompactNtpRttToTimeDelta(SaturatedToCompactNtp(TimeDelta::Micros(5'485))) + .us(), + 5'485, 16); + EXPECT_NEAR( + CompactNtpRttToTimeDelta(SaturatedToCompactNtp(TimeDelta::Micros(5'515))) + .us(), + 5'515, 16); +} + +TEST(TimeUtilTest, ToNtpUnits) { + EXPECT_EQ(ToNtpUnits(TimeDelta::Zero()), 0); + EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(1)), int64_t{1} << 32); + EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(-1)), -(int64_t{1} << 32)); + + EXPECT_EQ(ToNtpUnits(TimeDelta::Millis(500)), int64_t{1} << 31); + EXPECT_EQ(ToNtpUnits(TimeDelta::Millis(-1'500)), -(int64_t{3} << 31)); + + // Smallest TimeDelta that can be converted without precision loss. + EXPECT_EQ(ToNtpUnits(TimeDelta::Micros(15'625)), int64_t{1} << 26); + + // 1 us ~= 4'294.97 NTP units. ToNtpUnits makes no rounding promises. + EXPECT_GE(ToNtpUnits(TimeDelta::Micros(1)), 4'294); + EXPECT_LE(ToNtpUnits(TimeDelta::Micros(1)), 4'295); + + // Test near maximum and minimum supported values. + static constexpr int64_t k35MinutesInNtpUnits = int64_t{35 * 60} << 32; + EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(35 * 60)), k35MinutesInNtpUnits); + EXPECT_EQ(ToNtpUnits(TimeDelta::Seconds(-35 * 60)), -k35MinutesInNtpUnits); + + // The result for too large or too small values is unspecified, but + // shouldn't cause integer overflow or other undefined behavior. + ToNtpUnits(TimeDelta::Micros(std::numeric_limits::max() - 1)); + ToNtpUnits(TimeDelta::Micros(std::numeric_limits::min() + 1)); } } // namespace webrtc diff --git a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc index 159e21f9d2..736ccfbf6a 100644 --- a/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc +++ b/modules/rtp_rtcp/source/ulpfec_receiver_impl.cc @@ -177,7 +177,7 @@ int32_t UlpfecReceiverImpl::ProcessReceivedFec() { for (const auto& received_packet : received_packets) { // Send received media packet to VCM. if (!received_packet->is_fec) { - ForwardErrorCorrection::Packet* packet = received_packet->pkt; + ForwardErrorCorrection::Packet* packet = received_packet->pkt.get(); recovered_packet_callback_->OnRecoveredPacket(packet->data.data(), packet->data.size()); // Create a packet with the buffer to modify it. @@ -211,7 +211,7 @@ int32_t UlpfecReceiverImpl::ProcessReceivedFec() { // Already sent to the VCM and the jitter buffer. continue; } - ForwardErrorCorrection::Packet* packet = recovered_packet->pkt; + ForwardErrorCorrection::Packet* packet = recovered_packet->pkt.get(); ++packet_counter_.num_recovered_packets; // Set this flag first; in case the recovered packet carries a RED // header, OnRecoveredPacket will recurse back here. diff --git a/modules/utility/BUILD.gn b/modules/utility/BUILD.gn index aca7b1efdd..fc8512e27e 100644 --- a/modules/utility/BUILD.gn +++ b/modules/utility/BUILD.gn @@ -35,7 +35,12 @@ rtc_library("utility") { "../../api/task_queue", "../../common_audio", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:location", + "../../rtc_base:logging", + "../../rtc_base:platform_thread", + "../../rtc_base:rtc_event", + "../../rtc_base:timeutils", "../../rtc_base/system:arch", "../../system_wrappers", ] @@ -47,7 +52,7 @@ rtc_library("mock_process_thread") { sources = [ "include/mock/mock_process_thread.h" ] deps = [ ":utility", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:location", "../../test:test_support", ] } @@ -62,7 +67,8 @@ if (rtc_include_tests) { "..:module_api", "../../api/task_queue", "../../api/task_queue:task_queue_test", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:location", + "../../rtc_base:timeutils", "../../test:test_support", ] } diff --git a/modules/utility/source/process_thread_impl.cc b/modules/utility/source/process_thread_impl.cc index 73fc23400b..2274aaee91 100644 --- a/modules/utility/source/process_thread_impl.cc +++ b/modules/utility/source/process_thread_impl.cc @@ -171,7 +171,7 @@ void ProcessThreadImpl::PostDelayedTask(std::unique_ptr task, MutexLock lock(&mutex_); recalculate_wakeup_time = delayed_tasks_.empty() || run_at_ms < delayed_tasks_.top().run_at_ms; - delayed_tasks_.emplace(run_at_ms, std::move(task)); + delayed_tasks_.emplace(run_at_ms, sequence_id_++, std::move(task)); } if (recalculate_wakeup_time) { wake_up_.Set(); diff --git a/modules/utility/source/process_thread_impl.h b/modules/utility/source/process_thread_impl.h index e9a26eb96f..0dc7aff591 100644 --- a/modules/utility/source/process_thread_impl.h +++ b/modules/utility/source/process_thread_impl.h @@ -65,14 +65,22 @@ class ProcessThreadImpl : public ProcessThread { ModuleCallback& operator=(ModuleCallback&); }; struct DelayedTask { - DelayedTask(int64_t run_at_ms, std::unique_ptr task) - : run_at_ms(run_at_ms), task(task.release()) {} + DelayedTask(int64_t run_at_ms, + uint64_t sequence_id, + std::unique_ptr task) + : run_at_ms(run_at_ms), + sequence_id_(sequence_id), + task(task.release()) {} friend bool operator<(const DelayedTask& lhs, const DelayedTask& rhs) { // Earliest DelayedTask should be at the top of the priority queue. - return lhs.run_at_ms > rhs.run_at_ms; + if (lhs.run_at_ms != rhs.run_at_ms) { + return lhs.run_at_ms > rhs.run_at_ms; + } + return lhs.sequence_id_ > rhs.sequence_id_; } int64_t run_at_ms; + uint64_t sequence_id_; // DelayedTask owns the `task`, but some delayed tasks must be removed from // the std::priority_queue, but mustn't be deleted. std::priority_queue does // not give non-const access to the values, so storing unique_ptr would @@ -101,7 +109,10 @@ class ProcessThreadImpl : public ProcessThread { // Set to true when calling Process, to allow reentrant calls to WakeUp. bool holds_mutex_ RTC_GUARDED_BY(this) = false; std::queue queue_; + // `std::priority_queue` does not guarantee stable sort. For delayed tasks + // with the same wakeup time, use `sequence_id_` to ensure FIFO ordering. std::priority_queue delayed_tasks_ RTC_GUARDED_BY(mutex_); + uint64_t sequence_id_ RTC_GUARDED_BY(mutex_) = 0; // The `stop_` flag is modified only by the construction thread, protected by // `thread_checker_`. It is read also by the spawned `thread_`. The latter // thread must take `mutex_` before access, and for thread safety, the diff --git a/modules/video_capture/BUILD.gn b/modules/video_capture/BUILD.gn index 75c2648cae..616be5d53f 100644 --- a/modules/video_capture/BUILD.gn +++ b/modules/video_capture/BUILD.gn @@ -32,8 +32,12 @@ rtc_library("video_capture_module") { "../../api/video:video_rtp_headers", "../../common_video", "../../media:rtc_media_base", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:refcount", "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "//third_party/libyuv", @@ -47,8 +51,13 @@ if (!build_with_chromium) { deps = [ ":video_capture_module", "../../api:scoped_refptr", + "../../api:sequence_checker", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:refcount", + "../../rtc_base:stringutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", ] @@ -56,9 +65,11 @@ if (!build_with_chromium) { if (is_linux || is_chromeos) { sources = [ "linux/device_info_linux.cc", - "linux/device_info_linux.h", + "linux/device_info_v4l2.cc", + "linux/device_info_v4l2.h", "linux/video_capture_linux.cc", - "linux/video_capture_linux.h", + "linux/video_capture_v4l2.cc", + "linux/video_capture_v4l2.h", ] deps += [ "../../media:rtc_media_base" ] } @@ -131,7 +142,7 @@ if (!build_with_chromium) { "../../api/video:video_frame", "../../api/video:video_rtp_headers", "../../common_video", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../../test:frame_utils", diff --git a/modules/video_capture/OWNERS b/modules/video_capture/OWNERS index d728122343..364d66d36f 100644 --- a/modules/video_capture/OWNERS +++ b/modules/video_capture/OWNERS @@ -1,3 +1,4 @@ +ilnik@webrtc.org mflodman@webrtc.org perkj@webrtc.org tkchin@webrtc.org diff --git a/modules/video_capture/linux/device_info_linux.cc b/modules/video_capture/linux/device_info_linux.cc index cde3b86d5c..ccbbeae3ab 100644 --- a/modules/video_capture/linux/device_info_linux.cc +++ b/modules/video_capture/linux/device_info_linux.cc @@ -8,8 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/video_capture/linux/device_info_linux.h" - #include #include #include @@ -22,6 +20,7 @@ #include +#include "modules/video_capture/linux/device_info_v4l2.h" #include "modules/video_capture/video_capture.h" #include "modules/video_capture/video_capture_defines.h" #include "modules/video_capture/video_capture_impl.h" @@ -30,265 +29,7 @@ namespace webrtc { namespace videocapturemodule { VideoCaptureModule::DeviceInfo* VideoCaptureImpl::CreateDeviceInfo() { - return new videocapturemodule::DeviceInfoLinux(); -} - -DeviceInfoLinux::DeviceInfoLinux() : DeviceInfoImpl() {} - -int32_t DeviceInfoLinux::Init() { - return 0; -} - -DeviceInfoLinux::~DeviceInfoLinux() {} - -uint32_t DeviceInfoLinux::NumberOfDevices() { - uint32_t count = 0; - char device[20]; - int fd = -1; - struct v4l2_capability cap; - - /* detect /dev/video [0-63]VideoCaptureModule entries */ - for (int n = 0; n < 64; n++) { - sprintf(device, "/dev/video%d", n); - if ((fd = open(device, O_RDONLY)) != -1) { - // query device capabilities and make sure this is a video capture device - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || - !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { - close(fd); - continue; - } - - close(fd); - count++; - } - } - - return count; -} - -int32_t DeviceInfoLinux::GetDeviceName(uint32_t deviceNumber, - char* deviceNameUTF8, - uint32_t deviceNameLength, - char* deviceUniqueIdUTF8, - uint32_t deviceUniqueIdUTF8Length, - char* /*productUniqueIdUTF8*/, - uint32_t /*productUniqueIdUTF8Length*/) { - // Travel through /dev/video [0-63] - uint32_t count = 0; - char device[20]; - int fd = -1; - bool found = false; - struct v4l2_capability cap; - for (int n = 0; n < 64; n++) { - sprintf(device, "/dev/video%d", n); - if ((fd = open(device, O_RDONLY)) != -1) { - // query device capabilities and make sure this is a video capture device - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || - !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { - close(fd); - continue; - } - if (count == deviceNumber) { - // Found the device - found = true; - break; - } else { - close(fd); - count++; - } - } - } - - if (!found) - return -1; - - // query device capabilities - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { - RTC_LOG(LS_INFO) << "error in querying the device capability for device " - << device << ". errno = " << errno; - close(fd); - return -1; - } - - close(fd); - - char cameraName[64]; - memset(deviceNameUTF8, 0, deviceNameLength); - memcpy(cameraName, cap.card, sizeof(cap.card)); - - if (deviceNameLength > strlen(cameraName)) { - memcpy(deviceNameUTF8, cameraName, strlen(cameraName)); - } else { - RTC_LOG(LS_INFO) << "buffer passed is too small"; - return -1; - } - - if (cap.bus_info[0] != 0) // may not available in all drivers - { - // copy device id - if (deviceUniqueIdUTF8Length > strlen((const char*)cap.bus_info)) { - memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length); - memcpy(deviceUniqueIdUTF8, cap.bus_info, - strlen((const char*)cap.bus_info)); - } else { - RTC_LOG(LS_INFO) << "buffer passed is too small"; - return -1; - } - } - - return 0; -} - -int32_t DeviceInfoLinux::CreateCapabilityMap(const char* deviceUniqueIdUTF8) { - int fd; - char device[32]; - bool found = false; - - const int32_t deviceUniqueIdUTF8Length = - (int32_t)strlen((char*)deviceUniqueIdUTF8); - if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) { - RTC_LOG(LS_INFO) << "Device name too long"; - return -1; - } - RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device " - << deviceUniqueIdUTF8; - - /* detect /dev/video [0-63] entries */ - for (int n = 0; n < 64; ++n) { - sprintf(device, "/dev/video%d", n); - fd = open(device, O_RDONLY); - if (fd == -1) - continue; - - // query device capabilities - struct v4l2_capability cap; - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) { - // skip devices without video capture capability - if (!(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { - continue; - } - - if (cap.bus_info[0] != 0) { - if (strncmp((const char*)cap.bus_info, (const char*)deviceUniqueIdUTF8, - strlen((const char*)deviceUniqueIdUTF8)) == - 0) // match with device id - { - found = true; - break; // fd matches with device unique id supplied - } - } else // match for device name - { - if (IsDeviceNameMatches((const char*)cap.card, - (const char*)deviceUniqueIdUTF8)) { - found = true; - break; - } - } - } - close(fd); // close since this is not the matching device - } - - if (!found) { - RTC_LOG(LS_INFO) << "no matching device found"; - return -1; - } - - // now fd will point to the matching device - // reset old capability list. - _captureCapabilities.clear(); - - int size = FillCapabilities(fd); - close(fd); - - // Store the new used device name - _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; - _lastUsedDeviceName = - (char*)realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1); - memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, - _lastUsedDeviceNameLength + 1); - - RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size(); - - return size; -} - -int32_t DeviceInfoLinux::DisplayCaptureSettingsDialogBox( - const char* /*deviceUniqueIdUTF8*/, - const char* /*dialogTitleUTF8*/, - void* /*parentWindow*/, - uint32_t /*positionX*/, - uint32_t /*positionY*/) { - return -1; -} - -bool DeviceInfoLinux::IsDeviceNameMatches(const char* name, - const char* deviceUniqueIdUTF8) { - if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0) - return true; - return false; + return new videocapturemodule::DeviceInfoV4l2(); } - -int32_t DeviceInfoLinux::FillCapabilities(int fd) { - // set image format - struct v4l2_format video_fmt; - memset(&video_fmt, 0, sizeof(struct v4l2_format)); - - video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - video_fmt.fmt.pix.sizeimage = 0; - - int totalFmts = 4; - unsigned int videoFormats[] = {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY}; - - int sizes = 13; - unsigned int size[][2] = {{128, 96}, {160, 120}, {176, 144}, {320, 240}, - {352, 288}, {640, 480}, {704, 576}, {800, 600}, - {960, 720}, {1280, 720}, {1024, 768}, {1440, 1080}, - {1920, 1080}}; - - for (int fmts = 0; fmts < totalFmts; fmts++) { - for (int i = 0; i < sizes; i++) { - video_fmt.fmt.pix.pixelformat = videoFormats[fmts]; - video_fmt.fmt.pix.width = size[i][0]; - video_fmt.fmt.pix.height = size[i][1]; - - if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) { - if ((video_fmt.fmt.pix.width == size[i][0]) && - (video_fmt.fmt.pix.height == size[i][1])) { - VideoCaptureCapability cap; - cap.width = video_fmt.fmt.pix.width; - cap.height = video_fmt.fmt.pix.height; - if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) { - cap.videoType = VideoType::kYUY2; - } else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420) { - cap.videoType = VideoType::kI420; - } else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG) { - cap.videoType = VideoType::kMJPEG; - } else if (videoFormats[fmts] == V4L2_PIX_FMT_UYVY) { - cap.videoType = VideoType::kUYVY; - } - - // get fps of current camera mode - // V4l2 does not have a stable method of knowing so we just guess. - if (cap.width >= 800 && cap.videoType != VideoType::kMJPEG) { - cap.maxFPS = 15; - } else { - cap.maxFPS = 30; - } - - _captureCapabilities.push_back(cap); - RTC_LOG(LS_VERBOSE) << "Camera capability, width:" << cap.width - << " height:" << cap.height - << " type:" << static_cast(cap.videoType) - << " fps:" << cap.maxFPS; - } - } - } - } - - RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size(); - return _captureCapabilities.size(); -} - } // namespace videocapturemodule } // namespace webrtc diff --git a/modules/video_capture/linux/device_info_v4l2.cc b/modules/video_capture/linux/device_info_v4l2.cc new file mode 100644 index 0000000000..c1062d4078 --- /dev/null +++ b/modules/video_capture/linux/device_info_v4l2.cc @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_capture/linux/device_info_v4l2.h" + +#include +#include +#include +#include +#include +#include +#include +// v4l includes +#include + +#include + +#include "modules/video_capture/video_capture.h" +#include "modules/video_capture/video_capture_defines.h" +#include "modules/video_capture/video_capture_impl.h" +#include "rtc_base/logging.h" + +namespace webrtc { +namespace videocapturemodule { +DeviceInfoV4l2::DeviceInfoV4l2() : DeviceInfoImpl() {} + +int32_t DeviceInfoV4l2::Init() { + return 0; +} + +DeviceInfoV4l2::~DeviceInfoV4l2() {} + +uint32_t DeviceInfoV4l2::NumberOfDevices() { + uint32_t count = 0; + char device[20]; + int fd = -1; + struct v4l2_capability cap; + + /* detect /dev/video [0-63]VideoCaptureModule entries */ + for (int n = 0; n < 64; n++) { + snprintf(device, sizeof(device), "/dev/video%d", n); + if ((fd = open(device, O_RDONLY)) != -1) { + // query device capabilities and make sure this is a video capture device + if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || + !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { + close(fd); + continue; + } + + close(fd); + count++; + } + } + + return count; +} + +int32_t DeviceInfoV4l2::GetDeviceName(uint32_t deviceNumber, + char* deviceNameUTF8, + uint32_t deviceNameLength, + char* deviceUniqueIdUTF8, + uint32_t deviceUniqueIdUTF8Length, + char* /*productUniqueIdUTF8*/, + uint32_t /*productUniqueIdUTF8Length*/) { + // Travel through /dev/video [0-63] + uint32_t count = 0; + char device[20]; + int fd = -1; + bool found = false; + struct v4l2_capability cap; + for (int n = 0; n < 64; n++) { + snprintf(device, sizeof(device), "/dev/video%d", n); + if ((fd = open(device, O_RDONLY)) != -1) { + // query device capabilities and make sure this is a video capture device + if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0 || + !(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { + close(fd); + continue; + } + if (count == deviceNumber) { + // Found the device + found = true; + break; + } else { + close(fd); + count++; + } + } + } + + if (!found) + return -1; + + // query device capabilities + if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { + RTC_LOG(LS_INFO) << "error in querying the device capability for device " + << device << ". errno = " << errno; + close(fd); + return -1; + } + + close(fd); + + char cameraName[64]; + memset(deviceNameUTF8, 0, deviceNameLength); + memcpy(cameraName, cap.card, sizeof(cap.card)); + + if (deviceNameLength > strlen(cameraName)) { + memcpy(deviceNameUTF8, cameraName, strlen(cameraName)); + } else { + RTC_LOG(LS_INFO) << "buffer passed is too small"; + return -1; + } + + if (cap.bus_info[0] != 0) { // may not available in all drivers + // copy device id + size_t len = strlen(reinterpret_cast(cap.bus_info)); + if (deviceUniqueIdUTF8Length > len) { + memset(deviceUniqueIdUTF8, 0, deviceUniqueIdUTF8Length); + memcpy(deviceUniqueIdUTF8, cap.bus_info, len); + } else { + RTC_LOG(LS_INFO) << "buffer passed is too small"; + return -1; + } + } + + return 0; +} + +int32_t DeviceInfoV4l2::CreateCapabilityMap(const char* deviceUniqueIdUTF8) { + int fd; + char device[32]; + bool found = false; + + const int32_t deviceUniqueIdUTF8Length = strlen(deviceUniqueIdUTF8); + if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) { + RTC_LOG(LS_INFO) << "Device name too long"; + return -1; + } + RTC_LOG(LS_INFO) << "CreateCapabilityMap called for device " + << deviceUniqueIdUTF8; + + /* detect /dev/video [0-63] entries */ + for (int n = 0; n < 64; ++n) { + snprintf(device, sizeof(device), "/dev/video%d", n); + fd = open(device, O_RDONLY); + if (fd == -1) + continue; + + // query device capabilities + struct v4l2_capability cap; + if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) { + // skip devices without video capture capability + if (!(cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) { + continue; + } + + if (cap.bus_info[0] != 0) { + if (strncmp(reinterpret_cast(cap.bus_info), + deviceUniqueIdUTF8, + strlen(deviceUniqueIdUTF8)) == 0) { // match with device id + found = true; + break; // fd matches with device unique id supplied + } + } else { // match for device name + if (IsDeviceNameMatches(reinterpret_cast(cap.card), + deviceUniqueIdUTF8)) { + found = true; + break; + } + } + } + close(fd); // close since this is not the matching device + } + + if (!found) { + RTC_LOG(LS_INFO) << "no matching device found"; + return -1; + } + + // now fd will point to the matching device + // reset old capability list. + _captureCapabilities.clear(); + + int size = FillCapabilities(fd); + close(fd); + + // Store the new used device name + _lastUsedDeviceNameLength = deviceUniqueIdUTF8Length; + _lastUsedDeviceName = reinterpret_cast( + realloc(_lastUsedDeviceName, _lastUsedDeviceNameLength + 1)); + memcpy(_lastUsedDeviceName, deviceUniqueIdUTF8, + _lastUsedDeviceNameLength + 1); + + RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size(); + + return size; +} + +int32_t DeviceInfoV4l2::DisplayCaptureSettingsDialogBox( + const char* /*deviceUniqueIdUTF8*/, + const char* /*dialogTitleUTF8*/, + void* /*parentWindow*/, + uint32_t /*positionX*/, + uint32_t /*positionY*/) { + return -1; +} + +bool DeviceInfoV4l2::IsDeviceNameMatches(const char* name, + const char* deviceUniqueIdUTF8) { + if (strncmp(deviceUniqueIdUTF8, name, strlen(name)) == 0) + return true; + return false; +} + +int32_t DeviceInfoV4l2::FillCapabilities(int fd) { + // set image format + struct v4l2_format video_fmt; + memset(&video_fmt, 0, sizeof(struct v4l2_format)); + + video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + video_fmt.fmt.pix.sizeimage = 0; + + int totalFmts = 4; + unsigned int videoFormats[] = {V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY}; + + int sizes = 13; + unsigned int size[][2] = {{128, 96}, {160, 120}, {176, 144}, {320, 240}, + {352, 288}, {640, 480}, {704, 576}, {800, 600}, + {960, 720}, {1280, 720}, {1024, 768}, {1440, 1080}, + {1920, 1080}}; + + for (int fmts = 0; fmts < totalFmts; fmts++) { + for (int i = 0; i < sizes; i++) { + video_fmt.fmt.pix.pixelformat = videoFormats[fmts]; + video_fmt.fmt.pix.width = size[i][0]; + video_fmt.fmt.pix.height = size[i][1]; + + if (ioctl(fd, VIDIOC_TRY_FMT, &video_fmt) >= 0) { + if ((video_fmt.fmt.pix.width == size[i][0]) && + (video_fmt.fmt.pix.height == size[i][1])) { + VideoCaptureCapability cap; + cap.width = video_fmt.fmt.pix.width; + cap.height = video_fmt.fmt.pix.height; + if (videoFormats[fmts] == V4L2_PIX_FMT_YUYV) { + cap.videoType = VideoType::kYUY2; + } else if (videoFormats[fmts] == V4L2_PIX_FMT_YUV420) { + cap.videoType = VideoType::kI420; + } else if (videoFormats[fmts] == V4L2_PIX_FMT_MJPEG) { + cap.videoType = VideoType::kMJPEG; + } else if (videoFormats[fmts] == V4L2_PIX_FMT_UYVY) { + cap.videoType = VideoType::kUYVY; + } + + // get fps of current camera mode + // V4l2 does not have a stable method of knowing so we just guess. + if (cap.width >= 800 && cap.videoType != VideoType::kMJPEG) { + cap.maxFPS = 15; + } else { + cap.maxFPS = 30; + } + + _captureCapabilities.push_back(cap); + RTC_LOG(LS_VERBOSE) << "Camera capability, width:" << cap.width + << " height:" << cap.height + << " type:" << static_cast(cap.videoType) + << " fps:" << cap.maxFPS; + } + } + } + } + + RTC_LOG(LS_INFO) << "CreateCapabilityMap " << _captureCapabilities.size(); + return _captureCapabilities.size(); +} + +} // namespace videocapturemodule +} // namespace webrtc diff --git a/modules/video_capture/linux/device_info_linux.h b/modules/video_capture/linux/device_info_v4l2.h similarity index 85% rename from modules/video_capture/linux/device_info_linux.h rename to modules/video_capture/linux/device_info_v4l2.h index 304ae71230..fb95a6020d 100644 --- a/modules/video_capture/linux/device_info_linux.h +++ b/modules/video_capture/linux/device_info_v4l2.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ -#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ +#ifndef MODULES_VIDEO_CAPTURE_LINUX_DEVICE_INFO_V4L2_H_ +#define MODULES_VIDEO_CAPTURE_LINUX_DEVICE_INFO_V4L2_H_ #include @@ -17,10 +17,10 @@ namespace webrtc { namespace videocapturemodule { -class DeviceInfoLinux : public DeviceInfoImpl { +class DeviceInfoV4l2 : public DeviceInfoImpl { public: - DeviceInfoLinux(); - ~DeviceInfoLinux() override; + DeviceInfoV4l2(); + ~DeviceInfoV4l2() override; uint32_t NumberOfDevices() override; int32_t GetDeviceName(uint32_t deviceNumber, char* deviceNameUTF8, @@ -48,4 +48,4 @@ class DeviceInfoLinux : public DeviceInfoImpl { }; } // namespace videocapturemodule } // namespace webrtc -#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_DEVICE_INFO_LINUX_H_ +#endif // MODULES_VIDEO_CAPTURE_LINUX_DEVICE_INFO_V4L2_H_ diff --git a/modules/video_capture/linux/video_capture_linux.cc b/modules/video_capture/linux/video_capture_linux.cc index 10f9713ec3..2bc889facf 100644 --- a/modules/video_capture/linux/video_capture_linux.cc +++ b/modules/video_capture/linux/video_capture_linux.cc @@ -8,8 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/video_capture/linux/video_capture_linux.h" - #include #include #include @@ -26,6 +24,7 @@ #include "api/scoped_refptr.h" #include "media/base/video_common.h" +#include "modules/video_capture/linux/video_capture_v4l2.h" #include "modules/video_capture/video_capture.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" @@ -41,393 +40,5 @@ rtc::scoped_refptr VideoCaptureImpl::Create( return implementation; } - -VideoCaptureModuleV4L2::VideoCaptureModuleV4L2() - : VideoCaptureImpl(), - _deviceId(-1), - _deviceFd(-1), - _buffersAllocatedByDevice(-1), - _currentWidth(-1), - _currentHeight(-1), - _currentFrameRate(-1), - _captureStarted(false), - _captureVideoType(VideoType::kI420), - _pool(NULL) {} - -int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8) { - int len = strlen((const char*)deviceUniqueIdUTF8); - _deviceUniqueId = new (std::nothrow) char[len + 1]; - if (_deviceUniqueId) { - memcpy(_deviceUniqueId, deviceUniqueIdUTF8, len + 1); - } - - int fd; - char device[32]; - bool found = false; - - /* detect /dev/video [0-63] entries */ - int n; - for (n = 0; n < 64; n++) { - sprintf(device, "/dev/video%d", n); - if ((fd = open(device, O_RDONLY)) != -1) { - // query device capabilities - struct v4l2_capability cap; - if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) { - if (cap.bus_info[0] != 0) { - if (strncmp((const char*)cap.bus_info, - (const char*)deviceUniqueIdUTF8, - strlen((const char*)deviceUniqueIdUTF8)) == - 0) // match with device id - { - close(fd); - found = true; - break; // fd matches with device unique id supplied - } - } - } - close(fd); // close since this is not the matching device - } - } - if (!found) { - RTC_LOG(LS_INFO) << "no matching device found"; - return -1; - } - _deviceId = n; // store the device id - return 0; -} - -VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() { - StopCapture(); - if (_deviceFd != -1) - close(_deviceFd); -} - -int32_t VideoCaptureModuleV4L2::StartCapture( - const VideoCaptureCapability& capability) { - if (_captureStarted) { - if (capability.width == _currentWidth && - capability.height == _currentHeight && - _captureVideoType == capability.videoType) { - return 0; - } else { - StopCapture(); - } - } - - MutexLock lock(&capture_lock_); - // first open /dev/video device - char device[20]; - sprintf(device, "/dev/video%d", (int)_deviceId); - - if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) { - RTC_LOG(LS_INFO) << "error in opening " << device << " errono = " << errno; - return -1; - } - - // Supported video formats in preferred order. - // If the requested resolution is larger than VGA, we prefer MJPEG. Go for - // I420 otherwise. - const int nFormats = 5; - unsigned int fmts[nFormats]; - if (capability.width > 640 || capability.height > 480) { - fmts[0] = V4L2_PIX_FMT_MJPEG; - fmts[1] = V4L2_PIX_FMT_YUV420; - fmts[2] = V4L2_PIX_FMT_YUYV; - fmts[3] = V4L2_PIX_FMT_UYVY; - fmts[4] = V4L2_PIX_FMT_JPEG; - } else { - fmts[0] = V4L2_PIX_FMT_YUV420; - fmts[1] = V4L2_PIX_FMT_YUYV; - fmts[2] = V4L2_PIX_FMT_UYVY; - fmts[3] = V4L2_PIX_FMT_MJPEG; - fmts[4] = V4L2_PIX_FMT_JPEG; - } - - // Enumerate image formats. - struct v4l2_fmtdesc fmt; - int fmtsIdx = nFormats; - memset(&fmt, 0, sizeof(fmt)); - fmt.index = 0; - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - RTC_LOG(LS_INFO) << "Video Capture enumerats supported image formats:"; - while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) { - RTC_LOG(LS_INFO) << " { pixelformat = " - << cricket::GetFourccName(fmt.pixelformat) - << ", description = '" << fmt.description << "' }"; - // Match the preferred order. - for (int i = 0; i < nFormats; i++) { - if (fmt.pixelformat == fmts[i] && i < fmtsIdx) - fmtsIdx = i; - } - // Keep enumerating. - fmt.index++; - } - - if (fmtsIdx == nFormats) { - RTC_LOG(LS_INFO) << "no supporting video formats found"; - return -1; - } else { - RTC_LOG(LS_INFO) << "We prefer format " - << cricket::GetFourccName(fmts[fmtsIdx]); - } - - struct v4l2_format video_fmt; - memset(&video_fmt, 0, sizeof(struct v4l2_format)); - video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - video_fmt.fmt.pix.sizeimage = 0; - video_fmt.fmt.pix.width = capability.width; - video_fmt.fmt.pix.height = capability.height; - video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx]; - - if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - _captureVideoType = VideoType::kYUY2; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) - _captureVideoType = VideoType::kI420; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) - _captureVideoType = VideoType::kUYVY; - else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || - video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - _captureVideoType = VideoType::kMJPEG; - - // set format and frame size now - if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) { - RTC_LOG(LS_INFO) << "error in VIDIOC_S_FMT, errno = " << errno; - return -1; - } - - // initialize current width and height - _currentWidth = video_fmt.fmt.pix.width; - _currentHeight = video_fmt.fmt.pix.height; - - // Trying to set frame rate, before check driver capability. - bool driver_framerate_support = true; - struct v4l2_streamparm streamparms; - memset(&streamparms, 0, sizeof(streamparms)); - streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) { - RTC_LOG(LS_INFO) << "error in VIDIOC_G_PARM errno = " << errno; - driver_framerate_support = false; - // continue - } else { - // check the capability flag is set to V4L2_CAP_TIMEPERFRAME. - if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { - // driver supports the feature. Set required framerate. - memset(&streamparms, 0, sizeof(streamparms)); - streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - streamparms.parm.capture.timeperframe.numerator = 1; - streamparms.parm.capture.timeperframe.denominator = capability.maxFPS; - if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) { - RTC_LOG(LS_INFO) << "Failed to set the framerate. errno=" << errno; - driver_framerate_support = false; - } else { - _currentFrameRate = capability.maxFPS; - } - } - } - // If driver doesn't support framerate control, need to hardcode. - // Hardcoding the value based on the frame size. - if (!driver_framerate_support) { - if (_currentWidth >= 800 && _captureVideoType != VideoType::kMJPEG) { - _currentFrameRate = 15; - } else { - _currentFrameRate = 30; - } - } - - if (!AllocateVideoBuffers()) { - RTC_LOG(LS_INFO) << "failed to allocate video capture buffers"; - return -1; - } - - // start capture thread; - if (_captureThread.empty()) { - quit_ = false; - _captureThread = rtc::PlatformThread::SpawnJoinable( - [this] { - while (CaptureProcess()) { - } - }, - "CaptureThread", - rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kHigh)); - } - - // Needed to start UVC camera - from the uvcview application - enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) { - RTC_LOG(LS_INFO) << "Failed to turn on stream"; - return -1; - } - - _captureStarted = true; - return 0; -} - -int32_t VideoCaptureModuleV4L2::StopCapture() { - if (!_captureThread.empty()) { - { - MutexLock lock(&capture_lock_); - quit_ = true; - } - // Make sure the capture thread stops using the mutex. - _captureThread.Finalize(); - } - - MutexLock lock(&capture_lock_); - if (_captureStarted) { - _captureStarted = false; - - DeAllocateVideoBuffers(); - close(_deviceFd); - _deviceFd = -1; - } - - return 0; -} - -// critical section protected by the caller - -bool VideoCaptureModuleV4L2::AllocateVideoBuffers() { - struct v4l2_requestbuffers rbuffer; - memset(&rbuffer, 0, sizeof(v4l2_requestbuffers)); - - rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rbuffer.memory = V4L2_MEMORY_MMAP; - rbuffer.count = kNoOfV4L2Bufffers; - - if (ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer) < 0) { - RTC_LOG(LS_INFO) << "Could not get buffers from device. errno = " << errno; - return false; - } - - if (rbuffer.count > kNoOfV4L2Bufffers) - rbuffer.count = kNoOfV4L2Bufffers; - - _buffersAllocatedByDevice = rbuffer.count; - - // Map the buffers - _pool = new Buffer[rbuffer.count]; - - for (unsigned int i = 0; i < rbuffer.count; i++) { - struct v4l2_buffer buffer; - memset(&buffer, 0, sizeof(v4l2_buffer)); - buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buffer.memory = V4L2_MEMORY_MMAP; - buffer.index = i; - - if (ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer) < 0) { - return false; - } - - _pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, - MAP_SHARED, _deviceFd, buffer.m.offset); - - if (MAP_FAILED == _pool[i].start) { - for (unsigned int j = 0; j < i; j++) - munmap(_pool[j].start, _pool[j].length); - return false; - } - - _pool[i].length = buffer.length; - - if (ioctl(_deviceFd, VIDIOC_QBUF, &buffer) < 0) { - return false; - } - } - return true; -} - -bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() { - // unmap buffers - for (int i = 0; i < _buffersAllocatedByDevice; i++) - munmap(_pool[i].start, _pool[i].length); - - delete[] _pool; - - // turn off stream - enum v4l2_buf_type type; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(_deviceFd, VIDIOC_STREAMOFF, &type) < 0) { - RTC_LOG(LS_INFO) << "VIDIOC_STREAMOFF error. errno: " << errno; - } - - return true; -} - -bool VideoCaptureModuleV4L2::CaptureStarted() { - return _captureStarted; -} - -bool VideoCaptureModuleV4L2::CaptureProcess() { - int retVal = 0; - fd_set rSet; - struct timeval timeout; - - FD_ZERO(&rSet); - FD_SET(_deviceFd, &rSet); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - // _deviceFd written only in StartCapture, when this thread isn't running. - retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout); - if (retVal < 0 && errno != EINTR) // continue if interrupted - { - // select failed - return false; - } else if (retVal == 0) { - // select timed out - return true; - } else if (!FD_ISSET(_deviceFd, &rSet)) { - // not event on camera handle - return true; - } - - { - MutexLock lock(&capture_lock_); - - if (quit_) { - return false; - } - - if (_captureStarted) { - struct v4l2_buffer buf; - memset(&buf, 0, sizeof(struct v4l2_buffer)); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - // dequeue a buffer - repeat until dequeued properly! - while (ioctl(_deviceFd, VIDIOC_DQBUF, &buf) < 0) { - if (errno != EINTR) { - RTC_LOG(LS_INFO) << "could not sync on a buffer on device " - << strerror(errno); - return true; - } - } - VideoCaptureCapability frameInfo; - frameInfo.width = _currentWidth; - frameInfo.height = _currentHeight; - frameInfo.videoType = _captureVideoType; - - // convert to to I420 if needed - IncomingFrame((unsigned char*)_pool[buf.index].start, buf.bytesused, - frameInfo); - // enqueue the buffer again - if (ioctl(_deviceFd, VIDIOC_QBUF, &buf) == -1) { - RTC_LOG(LS_INFO) << "Failed to enqueue capture buffer"; - } - } - } - usleep(0); - return true; -} - -int32_t VideoCaptureModuleV4L2::CaptureSettings( - VideoCaptureCapability& settings) { - settings.width = _currentWidth; - settings.height = _currentHeight; - settings.maxFPS = _currentFrameRate; - settings.videoType = _captureVideoType; - - return 0; -} } // namespace videocapturemodule } // namespace webrtc diff --git a/modules/video_capture/linux/video_capture_v4l2.cc b/modules/video_capture/linux/video_capture_v4l2.cc new file mode 100644 index 0000000000..6bd3823ac4 --- /dev/null +++ b/modules/video_capture/linux/video_capture_v4l2.cc @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/video_capture/linux/video_capture_v4l2.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "api/scoped_refptr.h" +#include "media/base/video_common.h" +#include "modules/video_capture/video_capture.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" + +namespace webrtc { +namespace videocapturemodule { +VideoCaptureModuleV4L2::VideoCaptureModuleV4L2() + : VideoCaptureImpl(), + _deviceId(-1), + _deviceFd(-1), + _buffersAllocatedByDevice(-1), + _currentWidth(-1), + _currentHeight(-1), + _currentFrameRate(-1), + _captureStarted(false), + _captureVideoType(VideoType::kI420), + _pool(NULL) {} + +int32_t VideoCaptureModuleV4L2::Init(const char* deviceUniqueIdUTF8) { + int len = strlen((const char*)deviceUniqueIdUTF8); + _deviceUniqueId = new (std::nothrow) char[len + 1]; + if (_deviceUniqueId) { + memcpy(_deviceUniqueId, deviceUniqueIdUTF8, len + 1); + } + + int fd; + char device[32]; + bool found = false; + + /* detect /dev/video [0-63] entries */ + int n; + for (n = 0; n < 64; n++) { + snprintf(device, sizeof(device), "/dev/video%d", n); + if ((fd = open(device, O_RDONLY)) != -1) { + // query device capabilities + struct v4l2_capability cap; + if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) { + if (cap.bus_info[0] != 0) { + if (strncmp((const char*)cap.bus_info, + (const char*)deviceUniqueIdUTF8, + strlen((const char*)deviceUniqueIdUTF8)) == + 0) { // match with device id + close(fd); + found = true; + break; // fd matches with device unique id supplied + } + } + } + close(fd); // close since this is not the matching device + } + } + if (!found) { + RTC_LOG(LS_INFO) << "no matching device found"; + return -1; + } + _deviceId = n; // store the device id + return 0; +} + +VideoCaptureModuleV4L2::~VideoCaptureModuleV4L2() { + StopCapture(); + if (_deviceFd != -1) + close(_deviceFd); +} + +int32_t VideoCaptureModuleV4L2::StartCapture( + const VideoCaptureCapability& capability) { + if (_captureStarted) { + if (capability.width == _currentWidth && + capability.height == _currentHeight && + _captureVideoType == capability.videoType) { + return 0; + } else { + StopCapture(); + } + } + + MutexLock lock(&capture_lock_); + // first open /dev/video device + char device[20]; + snprintf(device, sizeof(device), "/dev/video%d", _deviceId); + + if ((_deviceFd = open(device, O_RDWR | O_NONBLOCK, 0)) < 0) { + RTC_LOG(LS_INFO) << "error in opening " << device << " errono = " << errno; + return -1; + } + + // Supported video formats in preferred order. + // If the requested resolution is larger than VGA, we prefer MJPEG. Go for + // I420 otherwise. + const int nFormats = 5; + unsigned int fmts[nFormats]; + if (capability.width > 640 || capability.height > 480) { + fmts[0] = V4L2_PIX_FMT_MJPEG; + fmts[1] = V4L2_PIX_FMT_YUV420; + fmts[2] = V4L2_PIX_FMT_YUYV; + fmts[3] = V4L2_PIX_FMT_UYVY; + fmts[4] = V4L2_PIX_FMT_JPEG; + } else { + fmts[0] = V4L2_PIX_FMT_YUV420; + fmts[1] = V4L2_PIX_FMT_YUYV; + fmts[2] = V4L2_PIX_FMT_UYVY; + fmts[3] = V4L2_PIX_FMT_MJPEG; + fmts[4] = V4L2_PIX_FMT_JPEG; + } + + // Enumerate image formats. + struct v4l2_fmtdesc fmt; + int fmtsIdx = nFormats; + memset(&fmt, 0, sizeof(fmt)); + fmt.index = 0; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + RTC_LOG(LS_INFO) << "Video Capture enumerats supported image formats:"; + while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) { + RTC_LOG(LS_INFO) << " { pixelformat = " + << cricket::GetFourccName(fmt.pixelformat) + << ", description = '" << fmt.description << "' }"; + // Match the preferred order. + for (int i = 0; i < nFormats; i++) { + if (fmt.pixelformat == fmts[i] && i < fmtsIdx) + fmtsIdx = i; + } + // Keep enumerating. + fmt.index++; + } + + if (fmtsIdx == nFormats) { + RTC_LOG(LS_INFO) << "no supporting video formats found"; + return -1; + } else { + RTC_LOG(LS_INFO) << "We prefer format " + << cricket::GetFourccName(fmts[fmtsIdx]); + } + + struct v4l2_format video_fmt; + memset(&video_fmt, 0, sizeof(struct v4l2_format)); + video_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + video_fmt.fmt.pix.sizeimage = 0; + video_fmt.fmt.pix.width = capability.width; + video_fmt.fmt.pix.height = capability.height; + video_fmt.fmt.pix.pixelformat = fmts[fmtsIdx]; + + if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + _captureVideoType = VideoType::kYUY2; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + _captureVideoType = VideoType::kI420; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) + _captureVideoType = VideoType::kUYVY; + else if (video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG || + video_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) + _captureVideoType = VideoType::kMJPEG; + + // set format and frame size now + if (ioctl(_deviceFd, VIDIOC_S_FMT, &video_fmt) < 0) { + RTC_LOG(LS_INFO) << "error in VIDIOC_S_FMT, errno = " << errno; + return -1; + } + + // initialize current width and height + _currentWidth = video_fmt.fmt.pix.width; + _currentHeight = video_fmt.fmt.pix.height; + + // Trying to set frame rate, before check driver capability. + bool driver_framerate_support = true; + struct v4l2_streamparm streamparms; + memset(&streamparms, 0, sizeof(streamparms)); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(_deviceFd, VIDIOC_G_PARM, &streamparms) < 0) { + RTC_LOG(LS_INFO) << "error in VIDIOC_G_PARM errno = " << errno; + driver_framerate_support = false; + // continue + } else { + // check the capability flag is set to V4L2_CAP_TIMEPERFRAME. + if (streamparms.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { + // driver supports the feature. Set required framerate. + memset(&streamparms, 0, sizeof(streamparms)); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + streamparms.parm.capture.timeperframe.numerator = 1; + streamparms.parm.capture.timeperframe.denominator = capability.maxFPS; + if (ioctl(_deviceFd, VIDIOC_S_PARM, &streamparms) < 0) { + RTC_LOG(LS_INFO) << "Failed to set the framerate. errno=" << errno; + driver_framerate_support = false; + } else { + _currentFrameRate = capability.maxFPS; + } + } + } + // If driver doesn't support framerate control, need to hardcode. + // Hardcoding the value based on the frame size. + if (!driver_framerate_support) { + if (_currentWidth >= 800 && _captureVideoType != VideoType::kMJPEG) { + _currentFrameRate = 15; + } else { + _currentFrameRate = 30; + } + } + + if (!AllocateVideoBuffers()) { + RTC_LOG(LS_INFO) << "failed to allocate video capture buffers"; + return -1; + } + + // start capture thread; + if (_captureThread.empty()) { + quit_ = false; + _captureThread = rtc::PlatformThread::SpawnJoinable( + [this] { + while (CaptureProcess()) { + } + }, + "CaptureThread", + rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kHigh)); + } + + // Needed to start UVC camera - from the uvcview application + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(_deviceFd, VIDIOC_STREAMON, &type) == -1) { + RTC_LOG(LS_INFO) << "Failed to turn on stream"; + return -1; + } + + _captureStarted = true; + return 0; +} + +int32_t VideoCaptureModuleV4L2::StopCapture() { + if (!_captureThread.empty()) { + { + MutexLock lock(&capture_lock_); + quit_ = true; + } + // Make sure the capture thread stops using the mutex. + _captureThread.Finalize(); + } + + MutexLock lock(&capture_lock_); + if (_captureStarted) { + _captureStarted = false; + + DeAllocateVideoBuffers(); + close(_deviceFd); + _deviceFd = -1; + } + + return 0; +} + +// critical section protected by the caller + +bool VideoCaptureModuleV4L2::AllocateVideoBuffers() { + struct v4l2_requestbuffers rbuffer; + memset(&rbuffer, 0, sizeof(v4l2_requestbuffers)); + + rbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rbuffer.memory = V4L2_MEMORY_MMAP; + rbuffer.count = kNoOfV4L2Bufffers; + + if (ioctl(_deviceFd, VIDIOC_REQBUFS, &rbuffer) < 0) { + RTC_LOG(LS_INFO) << "Could not get buffers from device. errno = " << errno; + return false; + } + + if (rbuffer.count > kNoOfV4L2Bufffers) + rbuffer.count = kNoOfV4L2Bufffers; + + _buffersAllocatedByDevice = rbuffer.count; + + // Map the buffers + _pool = new Buffer[rbuffer.count]; + + for (unsigned int i = 0; i < rbuffer.count; i++) { + struct v4l2_buffer buffer; + memset(&buffer, 0, sizeof(v4l2_buffer)); + buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buffer.memory = V4L2_MEMORY_MMAP; + buffer.index = i; + + if (ioctl(_deviceFd, VIDIOC_QUERYBUF, &buffer) < 0) { + return false; + } + + _pool[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, + MAP_SHARED, _deviceFd, buffer.m.offset); + + if (MAP_FAILED == _pool[i].start) { + for (unsigned int j = 0; j < i; j++) + munmap(_pool[j].start, _pool[j].length); + return false; + } + + _pool[i].length = buffer.length; + + if (ioctl(_deviceFd, VIDIOC_QBUF, &buffer) < 0) { + return false; + } + } + return true; +} + +bool VideoCaptureModuleV4L2::DeAllocateVideoBuffers() { + // unmap buffers + for (int i = 0; i < _buffersAllocatedByDevice; i++) + munmap(_pool[i].start, _pool[i].length); + + delete[] _pool; + + // turn off stream + enum v4l2_buf_type type; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl(_deviceFd, VIDIOC_STREAMOFF, &type) < 0) { + RTC_LOG(LS_INFO) << "VIDIOC_STREAMOFF error. errno: " << errno; + } + + return true; +} + +bool VideoCaptureModuleV4L2::CaptureStarted() { + return _captureStarted; +} + +bool VideoCaptureModuleV4L2::CaptureProcess() { + int retVal = 0; + fd_set rSet; + struct timeval timeout; + + FD_ZERO(&rSet); + FD_SET(_deviceFd, &rSet); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + // _deviceFd written only in StartCapture, when this thread isn't running. + retVal = select(_deviceFd + 1, &rSet, NULL, NULL, &timeout); + + { + MutexLock lock(&capture_lock_); + + if (quit_) { + return false; + } + + if (retVal < 0 && errno != EINTR) { // continue if interrupted + // select failed + return false; + } else if (retVal == 0) { + // select timed out + return true; + } else if (!FD_ISSET(_deviceFd, &rSet)) { + // not event on camera handle + return true; + } + + if (_captureStarted) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(struct v4l2_buffer)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + // dequeue a buffer - repeat until dequeued properly! + while (ioctl(_deviceFd, VIDIOC_DQBUF, &buf) < 0) { + if (errno != EINTR) { + RTC_LOG(LS_INFO) << "could not sync on a buffer on device " + << strerror(errno); + return true; + } + } + VideoCaptureCapability frameInfo; + frameInfo.width = _currentWidth; + frameInfo.height = _currentHeight; + frameInfo.videoType = _captureVideoType; + + // convert to to I420 if needed + IncomingFrame(reinterpret_cast(_pool[buf.index].start), + buf.bytesused, frameInfo); + // enqueue the buffer again + if (ioctl(_deviceFd, VIDIOC_QBUF, &buf) == -1) { + RTC_LOG(LS_INFO) << "Failed to enqueue capture buffer"; + } + } + } + usleep(0); + return true; +} + +int32_t VideoCaptureModuleV4L2::CaptureSettings( + VideoCaptureCapability& settings) { + settings.width = _currentWidth; + settings.height = _currentHeight; + settings.maxFPS = _currentFrameRate; + settings.videoType = _captureVideoType; + + return 0; +} +} // namespace videocapturemodule +} // namespace webrtc diff --git a/modules/video_capture/linux/video_capture_linux.h b/modules/video_capture/linux/video_capture_v4l2.h similarity index 88% rename from modules/video_capture/linux/video_capture_linux.h rename to modules/video_capture/linux/video_capture_v4l2.h index fa06d72b8d..65e89e2daa 100644 --- a/modules/video_capture/linux/video_capture_linux.h +++ b/modules/video_capture/linux/video_capture_v4l2.h @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ -#define MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ +#ifndef MODULES_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_V4L2_H_ +#define MODULES_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_V4L2_H_ #include #include @@ -62,4 +62,4 @@ class VideoCaptureModuleV4L2 : public VideoCaptureImpl { } // namespace videocapturemodule } // namespace webrtc -#endif // MODULES_VIDEO_CAPTURE_MAIN_SOURCE_LINUX_VIDEO_CAPTURE_LINUX_H_ +#endif // MODULES_VIDEO_CAPTURE_LINUX_VIDEO_CAPTURE_V4L2_H_ diff --git a/modules/video_capture/windows/sink_filter_ds.cc b/modules/video_capture/windows/sink_filter_ds.cc index 15f947a921..0c5acb668d 100644 --- a/modules/video_capture/windows/sink_filter_ds.cc +++ b/modules/video_capture/windows/sink_filter_ds.cc @@ -707,7 +707,7 @@ CaptureInputPin::GetAllocator(IMemAllocator** allocator) { return hr; allocator_.swap(allocator); } - *allocator = allocator_; + *allocator = allocator_.get(); allocator_->AddRef(); return S_OK; } diff --git a/modules/video_capture/windows/video_capture_ds.cc b/modules/video_capture/windows/video_capture_ds.cc index 1a1e51934d..781fbe9f0a 100644 --- a/modules/video_capture/windows/video_capture_ds.cc +++ b/modules/video_capture/windows/video_capture_ds.cc @@ -35,7 +35,7 @@ VideoCaptureDS::~VideoCaptureDS() { } if (_graphBuilder) { if (sink_filter_) - _graphBuilder->RemoveFilter(sink_filter_); + _graphBuilder->RemoveFilter(sink_filter_.get()); if (_captureFilter) _graphBuilder->RemoveFilter(_captureFilter); if (_dvFilter) @@ -101,13 +101,13 @@ int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8) { // Create the sink filte used for receiving Captured frames. sink_filter_ = new ComRefCount(this); - hr = _graphBuilder->AddFilter(sink_filter_, SINK_FILTER_NAME); + hr = _graphBuilder->AddFilter(sink_filter_.get(), SINK_FILTER_NAME); if (FAILED(hr)) { RTC_LOG(LS_INFO) << "Failed to add the send filter to the graph."; return -1; } - _inputSendPin = GetInputPin(sink_filter_); + _inputSendPin = GetInputPin(sink_filter_.get()); if (!_inputSendPin) { RTC_LOG(LS_INFO) << "Failed to get input send pin"; return -1; diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index 1a3d54e7b9..cd19bdfe84 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -24,7 +24,6 @@ rtc_library("encoded_frame") { "../../modules:module_api_public", "../../modules/rtp_rtcp:rtp_video_header", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base/experiments:alr_experiment", "../../rtc_base/experiments:rtt_mult_experiment", "../../rtc_base/system:rtc_export", @@ -81,19 +80,20 @@ rtc_library("nack_requester") { deps = [ "..:module_api", + "../../api:field_trials_view", "../../api:sequence_checker", "../../api/task_queue", "../../api/units:time_delta", "../../api/units:timestamp", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/task_utils:pending_task_safety_flag", "../../rtc_base/task_utils:repeating_task", "../../system_wrappers", - "../../system_wrappers:field_trial", "../utility", ] } @@ -112,8 +112,10 @@ rtc_library("packet_buffer") { "../../api/video:video_frame_type", "../../common_video", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", "../../rtc_base:logging", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:mod_ops", "../../rtc_base:rtc_numerics", "../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_video_header", @@ -139,8 +141,8 @@ rtc_library("h264_packet_buffer") { "../../api/video:video_frame_type", "../../common_video", "../../rtc_base:checks", + "../../rtc_base:copy_on_write_buffer", "../../rtc_base:logging", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_numerics", "../rtp_rtcp:rtp_rtcp_format", "../rtp_rtcp:rtp_video_header", @@ -160,43 +162,70 @@ rtc_library("frame_helpers") { absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector" ] } -rtc_library("frame_buffer") { +rtc_library("timing") { sources = [ - "frame_buffer3.cc", - "frame_buffer3.h", + "codec_timer.cc", + "codec_timer.h", + "timing.cc", + "timing.h", ] deps = [ - ":video_coding_utility", - "../../api/units:timestamp", - "../../api/video:encoded_frame", + "../../api:field_trials_view", + "../../api/units:time_delta", + "../../api/video:video_rtp_headers", "../../rtc_base:logging", + "../../rtc_base:macromagic", "../../rtc_base:rtc_numerics", - "../../system_wrappers:field_trial", + "../../rtc_base/experiments:field_trial_parser", + "../../rtc_base/synchronization:mutex", + "../../rtc_base/time:timestamp_extrapolator", + "../../system_wrappers", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("rtt_filter") { + sources = [ + "rtt_filter.cc", + "rtt_filter.h", ] + deps = [ "../../api/units:time_delta" ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/container:inlined_vector", - "//third_party/abseil-cpp/absl/types:optional", ] } -rtc_library("timing") { +rtc_library("jitter_estimator") { sources = [ - "codec_timer.cc", - "codec_timer.h", - "timing.cc", - "timing.h", + "jitter_estimator.cc", + "jitter_estimator.h", ] deps = [ + ":rtt_filter", + "../../api:field_trials_view", + "../../api/units:data_size", + "../../api/units:frequency", "../../api/units:time_delta", - "../../api/video:video_rtp_headers", - "../../rtc_base:macromagic", - "../../rtc_base:rtc_numerics", - "../../rtc_base/experiments:field_trial_parser", - "../../rtc_base/synchronization:mutex", - "../../rtc_base/time:timestamp_extrapolator", + "../../api/units:timestamp", + "../../rtc_base", + "../../rtc_base:safe_conversions", + "../../rtc_base/experiments:jitter_upper_bound_experiment", "../../system_wrappers", - "../../system_wrappers:field_trial", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("inter_frame_delay") { + sources = [ + "inter_frame_delay.cc", + "inter_frame_delay.h", + ] + deps = [ + "..:module_api_public", + "../../api/units:frequency", + "../../api/units:time_delta", + "../../api/units:timestamp", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -220,11 +249,7 @@ rtc_library("video_coding") { "h264_sps_pps_tracker.cc", "h264_sps_pps_tracker.h", "include/video_codec_initializer.h", - "inter_frame_delay.cc", - "inter_frame_delay.h", "internal_defines.h", - "jitter_estimator.cc", - "jitter_estimator.h", "loss_notification_controller.cc", "loss_notification_controller.h", "media_opt_util.cc", @@ -241,8 +266,6 @@ rtc_library("video_coding") { "rtp_vp8_ref_finder.h", "rtp_vp9_ref_finder.cc", "rtp_vp9_ref_finder.h", - "rtt_filter.cc", - "rtt_filter.h", "timestamp_map.cc", "timestamp_map.h", "unique_timestamp_counter.cc", @@ -255,9 +278,10 @@ rtc_library("video_coding") { deps = [ ":codec_globals_headers", ":encoded_frame", - ":frame_buffer", ":frame_helpers", - ":packet_buffer", + ":inter_frame_delay", + ":jitter_estimator", + ":rtt_filter", ":timing", ":video_codec_interface", ":video_coding_utility", @@ -267,12 +291,15 @@ rtc_library("video_coding") { "..:module_fec_api", "../../api:array_view", "../../api:fec_controller_api", + "../../api:field_trials_view", "../../api:rtp_headers", "../../api:rtp_packet_info", "../../api:scoped_refptr", "../../api:sequence_checker", "../../api/task_queue", "../../api/units:data_rate", + "../../api/units:data_size", + "../../api/units:frequency", "../../api/units:time_delta", "../../api/units:timestamp", "../../api/video:builtin_video_bitrate_allocator_factory", @@ -290,10 +317,16 @@ rtc_library("video_coding") { "../../common_video", "../../rtc_base", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:copy_on_write_buffer", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", + "../../rtc_base:safe_conversions", "../../rtc_base:threading", + "../../rtc_base:timeutils", "../../rtc_base/experiments:alr_experiment", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/experiments:jitter_upper_bound_experiment", @@ -369,14 +402,18 @@ rtc_library("video_coding_legacy") { deps = [ ":codec_globals_headers", ":encoded_frame", + ":inter_frame_delay", + ":jitter_estimator", ":timing", ":video_codec_interface", ":video_coding", "..:module_api", "..:module_api_public", + "../../api:field_trials_view", "../../api:rtp_headers", "../../api:rtp_packet_info", "../../api:sequence_checker", + "../../api/transport:field_trial_based_config", "../../api/units:timestamp", "../../api/video:encoded_image", "../../api/video:video_frame", @@ -386,9 +423,14 @@ rtc_library("video_coding_legacy") { "../../common_video", "../../modules/rtp_rtcp:rtp_video_header", "../../rtc_base:checks", + "../../rtc_base:event_tracer", + "../../rtc_base:location", "../../rtc_base:logging", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:one_time_event", "../../rtc_base:rtc_event", + "../../rtc_base:safe_conversions", + "../../rtc_base/memory:always_valid_pointer", "../../rtc_base/synchronization:mutex", "../../system_wrappers", "../rtp_rtcp:rtp_rtcp_format", @@ -448,6 +490,7 @@ rtc_library("video_coding_utility") { deps = [ ":video_codec_interface", "../../api:array_view", + "../../api:field_trials_view", "../../api:scoped_refptr", "../../api:sequence_checker", "../../api/video:encoded_frame", @@ -462,9 +505,13 @@ rtc_library("video_coding_utility") { "../../modules/rtp_rtcp", "../../rtc_base:bitstream_reader", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:rate_statistics", + "../../rtc_base:refcount", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../rtc_base:weak_ptr", "../../rtc_base/experiments:bandwidth_quality_scaler_settings", "../../rtc_base/experiments:encoder_info_settings", @@ -508,11 +555,15 @@ rtc_library("webrtc_h264") { "../../api/video:video_frame", "../../api/video:video_frame_i010", "../../api/video:video_rtp_headers", + "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", "../../common_video", "../../media:rtc_media_base", "../../rtc_base", "../../rtc_base:checks", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:timeutils", "../../rtc_base/system:rtc_export", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", @@ -559,6 +610,7 @@ rtc_library("webrtc_multiplex") { "../../media:rtc_media_base", "../../rtc_base", "../../rtc_base:checks", + "../../rtc_base:logging", "../../rtc_base/synchronization:mutex", "../rtp_rtcp:rtp_rtcp_format", ] @@ -614,8 +666,10 @@ rtc_library("webrtc_vp8") { "../../api/video_codecs:vp8_temporal_layers_factory", "../../common_video", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", "../../rtc_base:rtc_numerics", + "../../rtc_base:timeutils", "../../rtc_base/experiments:cpu_speed_experiment", "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:field_trial_parser", @@ -634,6 +688,14 @@ rtc_library("webrtc_vp8") { } } +rtc_source_set("webrtc_vp8_scalability") { + sources = [ + "codecs/vp8/vp8_scalability.cc", + "codecs/vp8/vp8_scalability.h", + ] + deps = [ "../../api/video_codecs:scalability_mode" ] +} + rtc_library("webrtc_vp8_temporal_layers") { visibility = [ "*" ] sources = [ @@ -653,8 +715,11 @@ rtc_library("webrtc_vp8_temporal_layers") { "../../api:fec_controller_api", "../../api/video_codecs:video_codecs_api", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:rate_statistics", "../../rtc_base:rtc_numerics", + "../../rtc_base:timeutils", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", ] @@ -703,24 +768,32 @@ rtc_library("webrtc_vp9") { ":webrtc_libvpx_interface", ":webrtc_vp9_helpers", "../../api:fec_controller_api", + "../../api:field_trials_view", "../../api:refcountedbase", "../../api:scoped_refptr", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../api/video:video_frame", "../../api/video:video_frame_i010", "../../api/video:video_rtp_headers", + "../../api/video_codecs:scalability_mode", "../../api/video_codecs:video_codecs_api", "../../common_video", "../../media:rtc_media_base", "../../rtc_base", + "../../rtc_base:buffer", "../../rtc_base:checks", + "../../rtc_base:event_tracer", + "../../rtc_base:logging", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", + "../../rtc_base/containers:flat_map", "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:field_trial_parser", "../../rtc_base/experiments:rate_control_settings", "../../rtc_base/synchronization:mutex", "../../system_wrappers:field_trial", "../rtp_rtcp:rtp_rtcp_format", + "svc:scalability_mode_util", "svc:scalability_structures", "svc:scalable_video_controller", "svc:svc_rate_allocator", @@ -746,13 +819,13 @@ if (rtc_include_tests) { deps = [ "../../api/video_codecs:video_codecs_api", + "../../modules/utility:utility", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", - "../../rtc_base:rtc_base_approved", + "../../sdk/android:internal_jni", "../../sdk/android:native_api_base", "../../sdk/android:native_api_codecs", "../../sdk/android:native_api_jni", - "//base", ] } } @@ -770,7 +843,6 @@ if (rtc_include_tests) { "../../api/video_codecs:video_codecs_api", "../../media:rtc_audio_video", "../../media:rtc_media_base", - "../../rtc_base:rtc_base_approved", "../../sdk:native_api", "../../sdk:peerconnectionfactory_base_objc", "../../sdk:videocodec_objc", @@ -819,7 +891,6 @@ if (rtc_include_tests) { "../../api/video_codecs:video_codecs_api", "../../common_video", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../test:test_support", ] } @@ -856,9 +927,12 @@ if (rtc_include_tests) { "../../api/video:video_rtp_headers", "../../api/video_codecs:video_codecs_api", "../../common_video", + "../../rtc_base:buffer", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:macromagic", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_task_queue", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:no_unique_address", "../../rtc_base/task_utils:to_queued_task", @@ -929,9 +1003,11 @@ if (rtc_include_tests) { "../../media:rtc_internal_video_codecs", "../../media:rtc_media_base", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:logging", "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", + "../../rtc_base:timeutils", "../../system_wrappers", "../../test:fileutils", "../../test:perf_test", @@ -1016,6 +1092,9 @@ if (rtc_include_tests) { "../../media:rtc_media_base", "../../media:rtc_simulcast_encoder_adapter", "../../rtc_base", + "../../rtc_base:refcount", + "../../rtc_base:stringutils", + "../../rtc_base:timeutils", "../../test:explicit_key_value_config", "../../test:field_trial", "../../test:fileutils", @@ -1066,13 +1145,13 @@ if (rtc_include_tests) { "decoding_state_unittest.cc", "fec_controller_unittest.cc", "frame_buffer2_unittest.cc", - "frame_buffer3_unittest.cc", "frame_dependencies_calculator_unittest.cc", "generic_decoder_unittest.cc", "h264_packet_buffer_unittest.cc", "h264_sprop_parameter_sets_unittest.cc", "h264_sps_pps_tracker_unittest.cc", "histogram_unittest.cc", + "inter_frame_delay_unittest.cc", "jitter_buffer_unittest.cc", "jitter_estimator_tests.cc", "loss_notification_controller_unittest.cc", @@ -1083,6 +1162,7 @@ if (rtc_include_tests) { "rtp_frame_reference_finder_unittest.cc", "rtp_vp8_ref_finder_unittest.cc", "rtp_vp9_ref_finder_unittest.cc", + "rtt_filter_unittest.cc", "session_info_unittest.cc", "test/stream_generator.cc", "test/stream_generator.h", @@ -1113,11 +1193,13 @@ if (rtc_include_tests) { ":chain_diff_calculator", ":codec_globals_headers", ":encoded_frame", - ":frame_buffer", ":frame_dependencies_calculator", ":h264_packet_buffer", + ":inter_frame_delay", + ":jitter_estimator", ":nack_requester", ":packet_buffer", + ":rtt_filter", ":simulcast_test_fixture_impl", ":timing", ":video_codec_interface", @@ -1144,7 +1226,10 @@ if (rtc_include_tests) { "../../api:videocodec_test_fixture_api", "../../api/task_queue:default_task_queue_factory", "../../api/test/video:function_video_factory", + "../../api/units:data_size", + "../../api/units:frequency", "../../api/units:time_delta", + "../../api/units:timestamp", "../../api/video:builtin_video_bitrate_allocator_factory", "../../api/video:encoded_frame", "../../api/video:render_resolution", @@ -1163,11 +1248,18 @@ if (rtc_include_tests) { "../../media:rtc_media_base", "../../rtc_base", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", + "../../rtc_base:histogram_percentile_counter", + "../../rtc_base:location", + "../../rtc_base:platform_thread", + "../../rtc_base:random", + "../../rtc_base:refcount", "../../rtc_base:rtc_base_tests_utils", + "../../rtc_base:rtc_event", "../../rtc_base:rtc_numerics", "../../rtc_base:rtc_task_queue", + "../../rtc_base:stringutils", "../../rtc_base:task_queue_for_test", + "../../rtc_base:timeutils", "../../rtc_base/experiments:encoder_info_settings", "../../rtc_base/experiments:jitter_upper_bound_experiment", "../../rtc_base/synchronization:mutex", @@ -1175,9 +1267,11 @@ if (rtc_include_tests) { "../../system_wrappers", "../../system_wrappers:field_trial", "../../system_wrappers:metrics", + "../../test:fake_encoded_frame", "../../test:fake_video_codecs", "../../test:field_trial", "../../test:fileutils", + "../../test:scoped_key_value_config", "../../test:test_common", "../../test:test_support", "../../test:video_test_common", diff --git a/modules/video_coding/codecs/av1/BUILD.gn b/modules/video_coding/codecs/av1/BUILD.gn index 2e4d824821..520ed0aee0 100644 --- a/modules/video_coding/codecs/av1/BUILD.gn +++ b/modules/video_coding/codecs/av1/BUILD.gn @@ -99,6 +99,7 @@ rtc_library("libaom_av1_encoder_if_supported") { sources = [ "libaom_av1_encoder_supported.cc" ] deps = [ "../../../../api/video_codecs:video_codecs_api", + "../../svc:scalability_mode_util", "../../svc:scalability_structures", "../../svc:scalable_video_controller", ] @@ -139,6 +140,7 @@ if (rtc_include_tests) { "../../../../api/units:data_size", "../../../../api/units:time_delta", "../../../../api/video:video_frame", + "../../svc:scalability_mode_util", "../../svc:scalability_structures", "../../svc:scalable_video_controller", ] diff --git a/modules/video_coding/codecs/av1/av1_svc_config.cc b/modules/video_coding/codecs/av1/av1_svc_config.cc index 2f1026b4df..465a03ab6f 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config.cc @@ -21,19 +21,27 @@ namespace webrtc { +bool LibaomAv1EncoderSupportsScalabilityMode(ScalabilityMode scalability_mode) { + // For libaom AV1, the scalability mode is supported if we can create the + // scalability structure. + return ScalabilityStructureConfig(scalability_mode) != absl::nullopt; +} + bool SetAv1SvcConfig(VideoCodec& video_codec) { RTC_DCHECK_EQ(video_codec.codecType, kVideoCodecAV1); - absl::string_view scalability_mode = video_codec.ScalabilityMode(); - if (scalability_mode.empty()) { - RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'NONE'."; - scalability_mode = "NONE"; + absl::optional scalability_mode = + video_codec.GetScalabilityMode(); + if (!scalability_mode.has_value()) { + RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'L1T1'."; + scalability_mode = ScalabilityMode::kL1T1; } std::unique_ptr structure = - CreateScalabilityStructure(scalability_mode); + CreateScalabilityStructure(*scalability_mode); if (structure == nullptr) { - RTC_LOG(LS_WARNING) << "Failed to create structure " << scalability_mode; + RTC_LOG(LS_WARNING) << "Failed to create structure " + << static_cast(*scalability_mode); return false; } diff --git a/modules/video_coding/codecs/av1/av1_svc_config.h b/modules/video_coding/codecs/av1/av1_svc_config.h index 15d94e03a9..c1d32736ed 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config.h +++ b/modules/video_coding/codecs/av1/av1_svc_config.h @@ -14,6 +14,8 @@ namespace webrtc { +bool LibaomAv1EncoderSupportsScalabilityMode(ScalabilityMode scalability_mode); + // Fills `video_codec.spatialLayers` using other members. bool SetAv1SvcConfig(VideoCodec& video_codec); diff --git a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc index d8aec65652..b4e264b5f7 100644 --- a/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc +++ b/modules/video_coding/codecs/av1/av1_svc_config_unittest.cc @@ -17,22 +17,10 @@ namespace webrtc { namespace { -TEST(Av1SvcConfigTest, RequireScalabilityMode) { +TEST(Av1SvcConfigTest, TreatsEmptyAsL1T1) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("Unknown"); - EXPECT_FALSE(SetAv1SvcConfig(video_codec)); - - video_codec.SetScalabilityMode("NONE"); - EXPECT_TRUE(SetAv1SvcConfig(video_codec)); -} - -TEST(Av1SvcConfigTest, TreatsEmptyAsNone) { - VideoCodec video_codec; - video_codec.codecType = kVideoCodecAV1; - - video_codec.SetScalabilityMode(""); EXPECT_TRUE(SetAv1SvcConfig(video_codec)); EXPECT_TRUE(video_codec.spatialLayers[0].active); @@ -43,7 +31,7 @@ TEST(Av1SvcConfigTest, TreatsEmptyAsNone) { TEST(Av1SvcConfigTest, SetsActiveSpatialLayersFromScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("L2T1"); + video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); EXPECT_TRUE(SetAv1SvcConfig(video_codec)); @@ -55,7 +43,7 @@ TEST(Av1SvcConfigTest, SetsActiveSpatialLayersFromScalabilityMode) { TEST(Av1SvcConfigTest, ConfiguresDobuleResolutionRatioFromScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("L2T1"); + video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); video_codec.width = 1200; video_codec.height = 800; @@ -71,7 +59,7 @@ TEST(Av1SvcConfigTest, ConfiguresSmallResolutionRatioFromScalabilityMode) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; // h mode uses 1.5:1 ratio - video_codec.SetScalabilityMode("L2T1h"); + video_codec.SetScalabilityMode(ScalabilityMode::kL2T1h); video_codec.width = 1500; video_codec.height = 900; @@ -87,7 +75,7 @@ TEST(Av1SvcConfigTest, CopiesFramrate) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; // h mode uses 1.5:1 ratio - video_codec.SetScalabilityMode("L2T1"); + video_codec.SetScalabilityMode(ScalabilityMode::kL2T1); video_codec.maxFramerate = 27; EXPECT_TRUE(SetAv1SvcConfig(video_codec)); @@ -99,7 +87,7 @@ TEST(Av1SvcConfigTest, CopiesFramrate) { TEST(Av1SvcConfigTest, SetsNumberOfTemporalLayers) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("L1T3"); + video_codec.SetScalabilityMode(ScalabilityMode::kL1T3); EXPECT_TRUE(SetAv1SvcConfig(video_codec)); @@ -109,7 +97,7 @@ TEST(Av1SvcConfigTest, SetsNumberOfTemporalLayers) { TEST(Av1SvcConfigTest, CopiesMinMaxBitrateForSingleSpatialLayer) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("L1T3"); + video_codec.SetScalabilityMode(ScalabilityMode::kL1T3); video_codec.minBitrate = 100; video_codec.maxBitrate = 500; @@ -126,7 +114,7 @@ TEST(Av1SvcConfigTest, CopiesMinMaxBitrateForSingleSpatialLayer) { TEST(Av1SvcConfigTest, SetsBitratesForMultipleSpatialLayers) { VideoCodec video_codec; video_codec.codecType = kVideoCodecAV1; - video_codec.SetScalabilityMode("L3T3"); + video_codec.SetScalabilityMode(ScalabilityMode::kL3T3); EXPECT_TRUE(SetAv1SvcConfig(video_codec)); diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder.cc b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc index 2405e2c52c..b05a1f7539 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_decoder.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_decoder.cc @@ -191,7 +191,7 @@ const char* LibaomAv1Decoder::ImplementationName() const { } // namespace -const bool kIsLibaomAv1DecoderSupported = true; +ABSL_CONST_INIT const bool kIsLibaomAv1DecoderSupported = true; std::unique_ptr CreateLibaomAv1Decoder() { return std::make_unique(); diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc index 79a31d9baa..0e1703ae1f 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.cc @@ -36,6 +36,13 @@ #include "third_party/libaom/source/libaom/aom/aom_encoder.h" #include "third_party/libaom/source/libaom/aom/aomcx.h" +#define SET_ENCODER_PARAM_OR_RETURN_ERROR(param_id, param_value) \ + do { \ + if (!SetEncoderControlParameters(param_id, param_value)) { \ + return WEBRTC_VIDEO_CODEC_ERROR; \ + } \ + } while (0) + namespace webrtc { namespace { @@ -49,21 +56,6 @@ constexpr int kLagInFrames = 0; // No look ahead. constexpr int kRtpTicksPerSecond = 90000; constexpr float kMinimumFrameRate = 1.0; -// Only positive speeds, range for real-time coding currently is: 6 - 8. -// Lower means slower/better quality, higher means fastest/lower quality. -int GetCpuSpeed(int width, int height, int number_of_cores) { - // For smaller resolutions, use lower speed setting (get some coding gain at - // the cost of increased encoding complexity). - if (number_of_cores > 4 && width * height < 320 * 180) - return 6; - else if (width * height >= 1280 * 720) - return 9; - else if (width * height >= 640 * 360) - return 8; - else - return 7; -} - aom_superblock_size_t GetSuperblockSize(int width, int height, int threads) { int resolution = width * height; if (threads >= 4 && resolution >= 960 * 540 && resolution < 1920 * 1080) @@ -74,7 +66,8 @@ aom_superblock_size_t GetSuperblockSize(int width, int height, int threads) { class LibaomAv1Encoder final : public VideoEncoder { public: - LibaomAv1Encoder(); + explicit LibaomAv1Encoder( + const absl::optional& aux_config); ~LibaomAv1Encoder(); int InitEncode(const VideoCodec* codec_settings, @@ -93,6 +86,12 @@ class LibaomAv1Encoder final : public VideoEncoder { EncoderInfo GetEncoderInfo() const override; private: + template + bool SetEncoderControlParameters(int param_id, P param_value); + + // Get value to be used for encoder cpu_speed setting + int GetCpuSpeed(int width, int height); + // Determine number of encoder threads to use. int NumberOfThreads(int width, int height, int number_of_cores); @@ -105,12 +104,15 @@ class LibaomAv1Encoder final : public VideoEncoder { // Configures the encoder which buffers next frame updates and can reference. void SetSvcRefFrameConfig( const ScalableVideoController::LayerFrameConfig& layer_frame); + // If pixel format doesn't match, then reallocate. + void MaybeRewrapImgWithFormat(const aom_img_fmt_t fmt); std::unique_ptr svc_controller_; bool inited_; bool rates_configured_; absl::optional svc_params_; VideoCodec encoder_settings_; + absl::optional aux_config_; aom_image_t* frame_for_encode_; aom_codec_ctx_t ctx_; aom_codec_enc_cfg_t cfg_; @@ -142,9 +144,11 @@ int32_t VerifyCodecSettings(const VideoCodec& codec_settings) { return WEBRTC_VIDEO_CODEC_OK; } -LibaomAv1Encoder::LibaomAv1Encoder() +LibaomAv1Encoder::LibaomAv1Encoder( + const absl::optional& aux_config) : inited_(false), rates_configured_(false), + aux_config_(aux_config), frame_for_encode_(nullptr), encoded_image_callback_(nullptr) {} @@ -179,15 +183,16 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, RTC_LOG(LS_WARNING) << "Simulcast is not implemented by LibaomAv1Encoder."; return result; } - absl::string_view scalability_mode = encoder_settings_.ScalabilityMode(); - if (scalability_mode.empty()) { - RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'NONE'."; - scalability_mode = "NONE"; + absl::optional scalability_mode = + encoder_settings_.GetScalabilityMode(); + if (!scalability_mode.has_value()) { + RTC_LOG(LS_WARNING) << "Scalability mode is not set, using 'L1T1'."; + scalability_mode = ScalabilityMode::kL1T1; } - svc_controller_ = CreateScalabilityStructure(scalability_mode); + svc_controller_ = CreateScalabilityStructure(*scalability_mode); if (svc_controller_ == nullptr) { RTC_LOG(LS_WARNING) << "Failed to set scalability mode " - << scalability_mode; + << static_cast(*scalability_mode); return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; } @@ -228,11 +233,10 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, cfg_.g_pass = AOM_RC_ONE_PASS; // One-pass rate control cfg_.g_lag_in_frames = kLagInFrames; // No look ahead when lag equals 0. - // Creating a wrapper to the image - setting image data to nullptr. Actual - // pointer will be set in encode. Setting align to 1, as it is meaningless - // (actual memory is not allocated). - frame_for_encode_ = - aom_img_alloc(nullptr, AOM_IMG_FMT_I420, cfg_.g_w, cfg_.g_h, 1); + if (frame_for_encode_ != nullptr) { + aom_img_free(frame_for_encode_); + frame_for_encode_ = nullptr; + } // Flag options: AOM_CODEC_USE_PSNR and AOM_CODEC_USE_HIGHBITDEPTH aom_codec_flags_t flags = 0; @@ -247,176 +251,125 @@ int LibaomAv1Encoder::InitEncode(const VideoCodec* codec_settings, inited_ = true; // Set control parameters - ret = aom_codec_control( - &ctx_, AOME_SET_CPUUSED, - GetCpuSpeed(cfg_.g_w, cfg_.g_h, settings.number_of_cores)); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_CPUUSED."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_CDEF, 1); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_CDEF."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_TPL_MODEL, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_TPL_MODEL."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_DELTAQ_MODE, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_DELTAQ_MODE."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_ORDER_HINT, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_ORDER_HINT."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_AQ_MODE, 3); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_AQ_MODE."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AOME_SET_MAX_INTRA_BITRATE_PCT, 300); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_MAX_INTRA_BITRATE_PCT."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_COEFF_COST_UPD_FREQ, 3); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_COEFF_COST_UPD_FREQ."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_MODE_COST_UPD_FREQ, 3); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_MODE_COST_UPD_FREQ."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - ret = aom_codec_control(&ctx_, AV1E_SET_MV_COST_UPD_FREQ, 3); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_MV_COST_UPD_FREQ."; - return WEBRTC_VIDEO_CODEC_ERROR; + SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_CPUUSED, + GetCpuSpeed(cfg_.g_w, cfg_.g_h)); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_CDEF, 1); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_TPL_MODEL, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_DELTAQ_MODE, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_ORDER_HINT, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_AQ_MODE, 3); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_SET_MAX_INTRA_BITRATE_PCT, 300); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_COEFF_COST_UPD_FREQ, 3); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MODE_COST_UPD_FREQ, 3); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MV_COST_UPD_FREQ, 3); + + if (codec_settings->mode == VideoCodecMode::kScreensharing) { + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TUNE_CONTENT, + AOM_CONTENT_SCREEN); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PALETTE, 1); + } else { + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PALETTE, 0); } if (cfg_.g_threads == 4 && cfg_.g_w == 640 && (cfg_.g_h == 360 || cfg_.g_h == 480)) { - ret = aom_codec_control(&ctx_, AV1E_SET_TILE_ROWS, - static_cast(log2(cfg_.g_threads))); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_TILE_ROWS."; - return WEBRTC_VIDEO_CODEC_ERROR; - } + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_ROWS, + static_cast(log2(cfg_.g_threads))); } else { - ret = aom_codec_control(&ctx_, AV1E_SET_TILE_COLUMNS, - static_cast(log2(cfg_.g_threads))); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_TILE_COLUMNS."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ROW_MT, 1); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ROW_MT."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_OBMC, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_OBMC."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_NOISE_SENSITIVITY, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_NOISE_SENSITIVITY."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_WARPED_MOTION, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_WARPED_MOTION."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_GLOBAL_MOTION, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_GLOBAL_MOTION."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_REF_FRAME_MVS, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_REF_FRAME_MVS."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = - aom_codec_control(&ctx_, AV1E_SET_SUPERBLOCK_SIZE, - GetSuperblockSize(cfg_.g_w, cfg_.g_h, cfg_.g_threads)); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_SUPERBLOCK_SIZE."; - return WEBRTC_VIDEO_CODEC_ERROR; - } + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_TILE_COLUMNS, + static_cast(log2(cfg_.g_threads))); + } + + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ROW_MT, 1); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_OBMC, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_NOISE_SENSITIVITY, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_WARPED_MOTION, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_GLOBAL_MOTION, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_REF_FRAME_MVS, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR( + AV1E_SET_SUPERBLOCK_SIZE, + GetSuperblockSize(cfg_.g_w, cfg_.g_h, cfg_.g_threads)); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_CFL_INTRA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_SMOOTH_INTRA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_ANGLE_DELTA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_FILTER_INTRA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_DISABLE_TRELLIS_QUANT, 1); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DIST_WTD_COMP, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DIFF_WTD_COMP, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_DUAL_FILTER, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTERINTRA_COMP, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTERINTRA_WEDGE, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTRA_EDGE_FILTER, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_INTRABC, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_MASKED_COMP, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_PAETH_INTRA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_QM, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_RECT_PARTITIONS, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_RESTORATION, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_SMOOTH_INTERINTRA, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ENABLE_TX64, 0); + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_MAX_REFERENCE_FRAMES, 3); - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_CFL_INTRA, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_CFL_INTRA."; - return WEBRTC_VIDEO_CODEC_ERROR; - } - - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_SMOOTH_INTRA, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_SMOOTH_INTRA."; - return WEBRTC_VIDEO_CODEC_ERROR; - } + return WEBRTC_VIDEO_CODEC_OK; +} - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_ANGLE_DELTA, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_ANGLE_DELTA."; - return WEBRTC_VIDEO_CODEC_ERROR; +template +bool LibaomAv1Encoder::SetEncoderControlParameters(int param_id, + P param_value) { + aom_codec_err_t error_code = aom_codec_control(&ctx_, param_id, param_value); + if (error_code != AOM_CODEC_OK) { + RTC_LOG(LS_WARNING) + << "LibaomAv1Encoder::SetEncoderControlParameters returned " + << error_code << " on id: " << param_id << "."; } + return error_code == AOM_CODEC_OK; +} - ret = aom_codec_control(&ctx_, AV1E_SET_ENABLE_FILTER_INTRA, 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AV1E_SET_ENABLE_FILTER_INTRA."; - return WEBRTC_VIDEO_CODEC_ERROR; - } +// Only positive speeds, range for real-time coding currently is: 6 - 8. +// Lower means slower/better quality, higher means fastest/lower quality. +int LibaomAv1Encoder::GetCpuSpeed(int width, int height) { + if (aux_config_) { + if (auto it = aux_config_->max_pixel_count_to_cpu_speed.lower_bound(width * + height); + it != aux_config_->max_pixel_count_to_cpu_speed.end()) { + return it->second; + } - ret = aom_codec_control(&ctx_, AV1E_SET_INTRA_DEFAULT_TX_ONLY, 1); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) - << "LibaomAv1Encoder::EncodeInit returned " << ret - << " on control AOM_CTRL_AV1E_SET_INTRA_DEFAULT_TX_ONLY."; - return WEBRTC_VIDEO_CODEC_ERROR; + return 10; + } else { + // For smaller resolutions, use lower speed setting (get some coding gain at + // the cost of increased encoding complexity). + switch (encoder_settings_.GetVideoEncoderComplexity()) { + case VideoCodecComplexity::kComplexityHigh: + if (width * height <= 320 * 180) + return 8; + else if (width * height <= 640 * 360) + return 9; + else + return 10; + case VideoCodecComplexity::kComplexityHigher: + if (width * height <= 320 * 180) + return 7; + else if (width * height <= 640 * 360) + return 8; + else if (width * height <= 1280 * 720) + return 9; + else + return 10; + case VideoCodecComplexity::kComplexityMax: + if (width * height <= 320 * 180) + return 6; + else if (width * height <= 640 * 360) + return 7; + else if (width * height <= 1280 * 720) + return 8; + else + return 9; + default: + return 10; + } } - - return WEBRTC_VIDEO_CODEC_OK; } int LibaomAv1Encoder::NumberOfThreads(int width, @@ -491,12 +444,7 @@ void LibaomAv1Encoder::SetSvcLayerId( aom_svc_layer_id_t layer_id = {}; layer_id.spatial_layer_id = layer_frame.SpatialId(); layer_id.temporal_layer_id = layer_frame.TemporalId(); - aom_codec_err_t ret = - aom_codec_control(&ctx_, AV1E_SET_SVC_LAYER_ID, &layer_id); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret - << " on control AV1E_SET_SVC_LAYER_ID."; - } + SetEncoderControlParameters(AV1E_SET_SVC_LAYER_ID, &layer_id); } void LibaomAv1Encoder::SetSvcRefFrameConfig( @@ -526,12 +474,8 @@ void LibaomAv1Encoder::SetSvcRefFrameConfig( ref_frame_config.refresh[buffer.id] = 1; } } - aom_codec_err_t ret = aom_codec_control(&ctx_, AV1E_SET_SVC_REF_FRAME_CONFIG, - &ref_frame_config); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret - << " on control AV1_SET_SVC_REF_FRAME_CONFIG."; - } + + SetEncoderControlParameters(AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_frame_config); } int32_t LibaomAv1Encoder::RegisterEncodeCompleteCallback( @@ -555,6 +499,21 @@ int32_t LibaomAv1Encoder::Release() { return WEBRTC_VIDEO_CODEC_OK; } +void LibaomAv1Encoder::MaybeRewrapImgWithFormat(const aom_img_fmt_t fmt) { + if (!frame_for_encode_) { + frame_for_encode_ = + aom_img_wrap(nullptr, fmt, cfg_.g_w, cfg_.g_h, 1, nullptr); + + } else if (frame_for_encode_->fmt != fmt) { + RTC_LOG(LS_INFO) << "Switching AV1 encoder pixel format to " + << (fmt == AOM_IMG_FMT_NV12 ? "NV12" : "I420"); + aom_img_free(frame_for_encode_); + frame_for_encode_ = + aom_img_wrap(nullptr, fmt, cfg_.g_w, cfg_.g_h, 1, nullptr); + } + // else no-op since the image is already in the right format. +} + int32_t LibaomAv1Encoder::Encode( const VideoFrame& frame, const std::vector* frame_types) { @@ -574,38 +533,73 @@ int32_t LibaomAv1Encoder::Encode( return WEBRTC_VIDEO_CODEC_ERROR; } + rtc::scoped_refptr buffer = frame.video_frame_buffer(); + absl::InlinedVector + supported_formats = {VideoFrameBuffer::Type::kI420, + VideoFrameBuffer::Type::kNV12}; + rtc::scoped_refptr mapped_buffer; + if (buffer->type() != VideoFrameBuffer::Type::kNative) { + // `buffer` is already mapped. + mapped_buffer = buffer; + } else { + // Attempt to map to one of the supported formats. + mapped_buffer = buffer->GetMappedFrameBuffer(supported_formats); + } + // Convert input frame to I420, if needed. - VideoFrame prepped_input_frame = frame; - if (prepped_input_frame.video_frame_buffer()->type() != - VideoFrameBuffer::Type::kI420 && - prepped_input_frame.video_frame_buffer()->type() != - VideoFrameBuffer::Type::kI420A) { - rtc::scoped_refptr converted_buffer( - prepped_input_frame.video_frame_buffer()->ToI420()); + if (!mapped_buffer || + (absl::c_find(supported_formats, mapped_buffer->type()) == + supported_formats.end() && + mapped_buffer->type() != VideoFrameBuffer::Type::kI420A)) { + rtc::scoped_refptr converted_buffer(buffer->ToI420()); if (!converted_buffer) { RTC_LOG(LS_ERROR) << "Failed to convert " << VideoFrameBufferTypeToString( - prepped_input_frame.video_frame_buffer()->type()) + frame.video_frame_buffer()->type()) << " image to I420. Can't encode frame."; return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; } RTC_CHECK(converted_buffer->type() == VideoFrameBuffer::Type::kI420 || converted_buffer->type() == VideoFrameBuffer::Type::kI420A); - prepped_input_frame = VideoFrame(converted_buffer, frame.timestamp(), - frame.render_time_ms(), frame.rotation()); - } - - // Set frame_for_encode_ data pointers and strides. - auto i420_buffer = prepped_input_frame.video_frame_buffer()->GetI420(); - frame_for_encode_->planes[AOM_PLANE_Y] = - const_cast(i420_buffer->DataY()); - frame_for_encode_->planes[AOM_PLANE_U] = - const_cast(i420_buffer->DataU()); - frame_for_encode_->planes[AOM_PLANE_V] = - const_cast(i420_buffer->DataV()); - frame_for_encode_->stride[AOM_PLANE_Y] = i420_buffer->StrideY(); - frame_for_encode_->stride[AOM_PLANE_U] = i420_buffer->StrideU(); - frame_for_encode_->stride[AOM_PLANE_V] = i420_buffer->StrideV(); + + mapped_buffer = converted_buffer; + } + + switch (mapped_buffer->type()) { + case VideoFrameBuffer::Type::kI420: + case VideoFrameBuffer::Type::kI420A: { + // Set frame_for_encode_ data pointers and strides. + MaybeRewrapImgWithFormat(AOM_IMG_FMT_I420); + auto i420_buffer = mapped_buffer->GetI420(); + RTC_DCHECK(i420_buffer); + frame_for_encode_->planes[AOM_PLANE_Y] = + const_cast(i420_buffer->DataY()); + frame_for_encode_->planes[AOM_PLANE_U] = + const_cast(i420_buffer->DataU()); + frame_for_encode_->planes[AOM_PLANE_V] = + const_cast(i420_buffer->DataV()); + frame_for_encode_->stride[AOM_PLANE_Y] = i420_buffer->StrideY(); + frame_for_encode_->stride[AOM_PLANE_U] = i420_buffer->StrideU(); + frame_for_encode_->stride[AOM_PLANE_V] = i420_buffer->StrideV(); + break; + } + case VideoFrameBuffer::Type::kNV12: { + MaybeRewrapImgWithFormat(AOM_IMG_FMT_NV12); + const NV12BufferInterface* nv12_buffer = mapped_buffer->GetNV12(); + RTC_DCHECK(nv12_buffer); + frame_for_encode_->planes[AOM_PLANE_Y] = + const_cast(nv12_buffer->DataY()); + frame_for_encode_->planes[AOM_PLANE_U] = + const_cast(nv12_buffer->DataUV()); + frame_for_encode_->planes[AOM_PLANE_V] = nullptr; + frame_for_encode_->stride[AOM_PLANE_Y] = nv12_buffer->StrideY(); + frame_for_encode_->stride[AOM_PLANE_U] = nv12_buffer->StrideUV(); + frame_for_encode_->stride[AOM_PLANE_V] = 0; + break; + } + default: + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } const uint32_t duration = kRtpTicksPerSecond / static_cast(encoder_settings_.maxFramerate); @@ -638,14 +632,8 @@ int32_t LibaomAv1Encoder::Encode( SetSvcLayerId(*layer_frame); SetSvcRefFrameConfig(*layer_frame); - aom_codec_err_t ret = - aom_codec_control(&ctx_, AV1E_SET_ERROR_RESILIENT_MODE, - layer_frame->TemporalId() > 0 ? 1 : 0); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret - << " on control AV1E_SET_ERROR_RESILIENT_MODE."; - return WEBRTC_VIDEO_CODEC_ERROR; - } + SET_ENCODER_PARAM_OR_RETURN_ERROR(AV1E_SET_ERROR_RESILIENT_MODE, + layer_frame->TemporalId() > 0 ? 1 : 0); } // Encode a frame. @@ -701,14 +689,11 @@ int32_t LibaomAv1Encoder::Encode( encoded_image._encodedHeight = cfg_.g_h; } encoded_image.timing_.flags = VideoSendTiming::kInvalid; + int qp = -1; - ret = aom_codec_control(&ctx_, AOME_GET_LAST_QUANTIZER, &qp); - if (ret != AOM_CODEC_OK) { - RTC_LOG(LS_WARNING) << "LibaomAv1Encoder::Encode returned " << ret - << " on control AOME_GET_LAST_QUANTIZER."; - return WEBRTC_VIDEO_CODEC_ERROR; - } + SET_ENCODER_PARAM_OR_RETURN_ERROR(AOME_GET_LAST_QUANTIZER, &qp); encoded_image.qp_ = qp; + encoded_image.SetColorSpace(frame.color_space()); ++data_pkt_count; } @@ -788,7 +773,7 @@ void LibaomAv1Encoder::SetRates(const RateControlParameters& parameters) { accumulated_bitrate_bps / 1000; } } - aom_codec_control(&ctx_, AV1E_SET_SVC_PARAMS, &*svc_params_); + SetEncoderControlParameters(AV1E_SET_SVC_PARAMS, &*svc_params_); } rates_configured_ = true; @@ -805,7 +790,8 @@ VideoEncoder::EncoderInfo LibaomAv1Encoder::GetEncoderInfo() const { info.has_trusted_rate_controller = true; info.is_hardware_accelerated = false; info.scaling_settings = VideoEncoder::ScalingSettings(kMinQindex, kMaxQindex); - info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420}; + info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420, + VideoFrameBuffer::Type::kNV12}; if (SvcEnabled()) { for (int sid = 0; sid < svc_params_->number_spatial_layers; ++sid) { info.fps_allocation[sid].resize(svc_params_->number_temporal_layers); @@ -821,7 +807,12 @@ VideoEncoder::EncoderInfo LibaomAv1Encoder::GetEncoderInfo() const { } // namespace std::unique_ptr CreateLibaomAv1Encoder() { - return std::make_unique(); + return std::make_unique(absl::nullopt); +} + +std::unique_ptr CreateLibaomAv1Encoder( + const LibaomAv1EncoderAuxConfig& aux_config) { + return std::make_unique(aux_config); } } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder.h b/modules/video_coding/codecs/av1/libaom_av1_encoder.h index e69df9e8bb..2fd1d5a754 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder.h +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder.h @@ -10,13 +10,22 @@ #ifndef MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_H_ #define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_H_ +#include #include #include "absl/strings/string_view.h" #include "api/video_codecs/video_encoder.h" namespace webrtc { +struct LibaomAv1EncoderAuxConfig { + // A map of max pixel count --> cpu speed. + std::map max_pixel_count_to_cpu_speed; +}; + std::unique_ptr CreateLibaomAv1Encoder(); +std::unique_ptr CreateLibaomAv1Encoder( + const LibaomAv1EncoderAuxConfig& aux_config); + } // namespace webrtc #endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc index 0bb31085e2..900eb137af 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.cc @@ -9,33 +9,21 @@ */ #include "modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h" -#include "modules/video_coding/svc/create_scalability_structure.h" - #if defined(RTC_USE_LIBAOM_AV1_ENCODER) #include "modules/video_coding/codecs/av1/libaom_av1_encoder.h" // nogncheck #endif namespace webrtc { #if defined(RTC_USE_LIBAOM_AV1_ENCODER) -const bool kIsLibaomAv1EncoderSupported = true; +ABSL_CONST_INIT const bool kIsLibaomAv1EncoderSupported = true; std::unique_ptr CreateLibaomAv1EncoderIfSupported() { return CreateLibaomAv1Encoder(); } -bool LibaomAv1EncoderSupportsScalabilityMode( - absl::string_view scalability_mode) { - // For libaom AV1, the scalability mode is supported if we can create the - // scalability structure. - return ScalabilityStructureConfig(scalability_mode) != absl::nullopt; -} #else const bool kIsLibaomAv1EncoderSupported = false; std::unique_ptr CreateLibaomAv1EncoderIfSupported() { return nullptr; } -bool LibaomAv1EncoderSupportsScalabilityMode( - absl::string_view scalability_mode) { - return false; -} #endif } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h index 84dd8d6002..d27ac893c6 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h @@ -21,8 +21,6 @@ namespace webrtc { ABSL_CONST_INIT extern const bool kIsLibaomAv1EncoderSupported; std::unique_ptr CreateLibaomAv1EncoderIfSupported(); -bool LibaomAv1EncoderSupportsScalabilityMode( - absl::string_view scalability_mode); } // namespace webrtc diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc index 0c67e4dde4..5243edc1e4 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_encoder_unittest.cc @@ -64,7 +64,7 @@ TEST(LibaomAv1EncoderTest, NoBitrateOnTopLayerRefecltedInActiveDecodeTargets) { // Configure encoder with 2 temporal layers. std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode("L1T2"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL1T2); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); @@ -90,7 +90,7 @@ TEST(LibaomAv1EncoderTest, SpatialScalabilityInTemporalUnitReportedAsDeltaFrame) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode("L2T1"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); @@ -112,7 +112,7 @@ TEST(LibaomAv1EncoderTest, TEST(LibaomAv1EncoderTest, NoBitrateOnTopSpatialLayerProduceDeltaFrames) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode("L2T1"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL2T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); @@ -140,7 +140,7 @@ TEST(LibaomAv1EncoderTest, SetsEndOfPictureForLastFrameInTemporalUnit) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. - codec_settings.SetScalabilityMode("L3T1"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); codec_settings.maxBitrate = allocation.get_sum_kbps(); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); @@ -167,7 +167,7 @@ TEST(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); // Configure encoder with 3 spatial layers. - codec_settings.SetScalabilityMode("L3T1"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); // Odd width and height values should not make encoder crash. codec_settings.width = 623; codec_settings.height = 405; @@ -186,7 +186,7 @@ TEST(LibaomAv1EncoderTest, CheckOddDimensionsWithSpatialLayers) { TEST(LibaomAv1EncoderTest, EncoderInfoProvidesFpsAllocation) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode("L3T3"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL3T3); codec_settings.maxFramerate = 60; ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); @@ -208,7 +208,7 @@ TEST(LibaomAv1EncoderTest, PopulatesEncodedFrameSize) { codec_settings.maxBitrate = allocation.get_sum_kbps(); ASSERT_GT(codec_settings.width, 4); // Configure encoder with 3 spatial layers. - codec_settings.SetScalabilityMode("L3T1"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL3T1); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); encoder->SetRates(VideoEncoder::RateControlParameters( diff --git a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc index f97808e631..5d9c251bc7 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_unittest.cc +++ b/modules/video_coding/codecs/av1/libaom_av1_unittest.cc @@ -28,6 +28,7 @@ #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/include/video_error_codes.h" #include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "modules/video_coding/svc/scalable_video_controller.h" #include "modules/video_coding/svc/scalable_video_controller_no_layering.h" #include "test/gmock.h" @@ -55,7 +56,7 @@ constexpr int kFramerate = 30; VideoCodec DefaultCodecSettings() { VideoCodec codec_settings; - codec_settings.SetScalabilityMode("NONE"); + codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); codec_settings.width = kWidth; codec_settings.height = kHeight; codec_settings.maxFramerate = kFramerate; @@ -175,6 +176,13 @@ struct LayerId { }; struct SvcTestParam { + ScalabilityMode GetScalabilityMode() const { + absl::optional scalability_mode = + ScalabilityModeFromString(name); + RTC_CHECK(scalability_mode.has_value()); + return *scalability_mode; + } + std::string name; int num_frames_to_generate; std::map configured_bitrates; @@ -185,7 +193,7 @@ class LibaomAv1SvcTest : public ::testing::TestWithParam {}; TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) { const SvcTestParam param = GetParam(); std::unique_ptr svc_controller = - CreateScalabilityStructure(param.name); + CreateScalabilityStructure(param.GetScalabilityMode()); ASSERT_TRUE(svc_controller); VideoBitrateAllocation allocation; if (param.configured_bitrates.empty()) { @@ -208,7 +216,7 @@ TEST_P(LibaomAv1SvcTest, EncodeAndDecodeAllDecodeTargets) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode(GetParam().name); + codec_settings.SetScalabilityMode(GetParam().GetScalabilityMode()); ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), WEBRTC_VIDEO_CODEC_OK); encoder->SetRates(VideoEncoder::RateControlParameters( @@ -278,7 +286,7 @@ TEST_P(LibaomAv1SvcTest, SetRatesMatchMeasuredBitrate) { std::unique_ptr encoder = CreateLibaomAv1Encoder(); ASSERT_TRUE(encoder); VideoCodec codec_settings = DefaultCodecSettings(); - codec_settings.SetScalabilityMode(param.name); + codec_settings.SetScalabilityMode(param.GetScalabilityMode()); codec_settings.maxBitrate = allocation.get_sum_kbps(); codec_settings.maxFramerate = 30; ASSERT_EQ(encoder->InitEncode(&codec_settings, DefaultEncoderSettings()), @@ -318,7 +326,7 @@ TEST_P(LibaomAv1SvcTest, SetRatesMatchMeasuredBitrate) { INSTANTIATE_TEST_SUITE_P( Svc, LibaomAv1SvcTest, - Values(SvcTestParam{"NONE", /*num_frames_to_generate=*/4}, + Values(SvcTestParam{"L1T1", /*num_frames_to_generate=*/4}, SvcTestParam{"L1T2", /*num_frames_to_generate=*/4, /*configured_bitrates=*/ diff --git a/modules/video_coding/codecs/h264/h264.cc b/modules/video_coding/codecs/h264/h264.cc index 2ac19ba0b6..b77cf0d149 100644 --- a/modules/video_coding/codecs/h264/h264.cc +++ b/modules/video_coding/codecs/h264/h264.cc @@ -44,7 +44,8 @@ bool IsH264CodecSupported() { #endif } -constexpr absl::string_view kSupportedScalabilityModes[] = {"L1T2", "L1T3"}; +constexpr ScalabilityMode kSupportedScalabilityModes[] = { + ScalabilityMode::kL1T1, ScalabilityMode::kL1T2, ScalabilityMode::kL1T3}; } // namespace @@ -127,7 +128,7 @@ bool H264Encoder::IsSupported() { return IsH264CodecSupported(); } -bool H264Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { +bool H264Encoder::SupportsScalabilityMode(ScalabilityMode scalability_mode) { for (const auto& entry : kSupportedScalabilityModes) { if (entry == scalability_mode) { return true; diff --git a/modules/video_coding/codecs/h264/h264_decoder_impl.cc b/modules/video_coding/codecs/h264/h264_decoder_impl.cc index 31279b7379..2ed63dc0a1 100644 --- a/modules/video_coding/codecs/h264/h264_decoder_impl.cc +++ b/modules/video_coding/codecs/h264/h264_decoder_impl.cc @@ -41,10 +41,9 @@ namespace webrtc { namespace { -constexpr std::array kPixelFormatsDefault = { - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P}; -constexpr std::array kPixelFormatsFullRange = { - AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P}; +constexpr std::array kPixelFormatsSupported = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P}; const size_t kYPlaneIndex = 0; const size_t kUPlaneIndex = 1; const size_t kVPlaneIndex = 2; @@ -78,17 +77,11 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, // Necessary capability to be allowed to provide our own buffers. RTC_DCHECK(context->codec->capabilities | AV_CODEC_CAP_DR1); - // Limited or full range YUV420 or YUV444 is expected. - auto pixelFormatDefault = std::find_if( - kPixelFormatsDefault.begin(), kPixelFormatsDefault.end(), - [context](AVPixelFormat format) { return context->pix_fmt == format; }); - auto pixelFormatFullRange = std::find_if( - kPixelFormatsFullRange.begin(), kPixelFormatsFullRange.end(), + auto pixelFormatSupported = std::find_if( + kPixelFormatsSupported.begin(), kPixelFormatsSupported.end(), [context](AVPixelFormat format) { return context->pix_fmt == format; }); - // Limited or full range YUV420 is expected. - RTC_CHECK(pixelFormatDefault != kPixelFormatsDefault.end() || - pixelFormatFullRange != kPixelFormatsFullRange.end()); + RTC_CHECK(pixelFormatSupported != kPixelFormatsSupported.end()); // `av_frame->width` and `av_frame->height` are set by FFmpeg. These are the // actual image's dimensions and may be different from `context->width` and @@ -125,6 +118,7 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, rtc::scoped_refptr frame_buffer; rtc::scoped_refptr i444_buffer; rtc::scoped_refptr i420_buffer; + rtc::scoped_refptr i422_buffer; switch (context->pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: @@ -153,6 +147,19 @@ int H264DecoderImpl::AVGetBuffer2(AVCodecContext* context, av_frame->linesize[kVPlaneIndex] = i444_buffer->StrideV(); frame_buffer = i444_buffer; break; + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUVJ422P: + i422_buffer = + decoder->ffmpeg_buffer_pool_.CreateI422Buffer(width, height); + // Set `av_frame` members as required by FFmpeg. + av_frame->data[kYPlaneIndex] = i422_buffer->MutableDataY(); + av_frame->linesize[kYPlaneIndex] = i422_buffer->StrideY(); + av_frame->data[kUPlaneIndex] = i422_buffer->MutableDataU(); + av_frame->linesize[kUPlaneIndex] = i422_buffer->StrideU(); + av_frame->data[kVPlaneIndex] = i422_buffer->MutableDataV(); + av_frame->linesize[kVPlaneIndex] = i422_buffer->StrideV(); + frame_buffer = i422_buffer; + break; default: RTC_LOG(LS_ERROR) << "Unsupported buffer type " << context->pix_fmt << ". Check supported supported pixel formats!"; @@ -363,9 +370,12 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, case VideoFrameBuffer::Type::kI444: planar_yuv8_buffer = frame_buffer->GetI444(); break; + case VideoFrameBuffer::Type::kI422: + planar_yuv8_buffer = frame_buffer->GetI422(); + break; default: // If this code is changed to allow other video frame buffer type, - // make sure that the code below which wraps I420/I444 buffer and + // make sure that the code below which wraps I420/I422/I444 buffer and // code which converts to NV12 is changed // to work with new video frame buffer type @@ -400,22 +410,40 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, 2); rtc::scoped_refptr cropped_buffer; - if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) { - cropped_buffer = WrapI420Buffer( - av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], - av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], - av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], - av_frame_->linesize[kVPlaneIndex], - // To keep reference alive. - [frame_buffer] {}); - } else { - cropped_buffer = WrapI444Buffer( - av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], - av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], - av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], - av_frame_->linesize[kVPlaneIndex], - // To keep reference alive. - [frame_buffer] {}); + switch (video_frame_buffer_type) { + case VideoFrameBuffer::Type::kI420: + cropped_buffer = WrapI420Buffer( + av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], + av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], + av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], + av_frame_->linesize[kVPlaneIndex], + // To keep reference alive. + [frame_buffer] {}); + break; + case VideoFrameBuffer::Type::kI444: + cropped_buffer = WrapI444Buffer( + av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], + av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], + av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], + av_frame_->linesize[kVPlaneIndex], + // To keep reference alive. + [frame_buffer] {}); + break; + case VideoFrameBuffer::Type::kI422: + cropped_buffer = WrapI422Buffer( + av_frame_->width, av_frame_->height, av_frame_->data[kYPlaneIndex], + av_frame_->linesize[kYPlaneIndex], av_frame_->data[kUPlaneIndex], + av_frame_->linesize[kUPlaneIndex], av_frame_->data[kVPlaneIndex], + av_frame_->linesize[kVPlaneIndex], + // To keep reference alive. + [frame_buffer] {}); + break; + default: + RTC_LOG(LS_ERROR) << "frame_buffer type: " + << static_cast(video_frame_buffer_type) + << " is not supported!"; + ReportError(); + return WEBRTC_VIDEO_CODEC_ERROR; } if (preferred_output_format_ == VideoFrameBuffer::Type::kNV12) { @@ -423,30 +451,53 @@ int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, cropped_buffer->width(), cropped_buffer->height()); const PlanarYuv8Buffer* cropped_planar_yuv8_buffer = nullptr; - if (video_frame_buffer_type == VideoFrameBuffer::Type::kI420) { - cropped_planar_yuv8_buffer = cropped_buffer->GetI420(); - libyuv::I420ToNV12(cropped_planar_yuv8_buffer->DataY(), - cropped_planar_yuv8_buffer->StrideY(), - cropped_planar_yuv8_buffer->DataU(), - cropped_planar_yuv8_buffer->StrideU(), - cropped_planar_yuv8_buffer->DataV(), - cropped_planar_yuv8_buffer->StrideV(), - nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), - nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(), - planar_yuv8_buffer->width(), - planar_yuv8_buffer->height()); - } else { - cropped_planar_yuv8_buffer = cropped_buffer->GetI444(); - libyuv::I444ToNV12(cropped_planar_yuv8_buffer->DataY(), - cropped_planar_yuv8_buffer->StrideY(), - cropped_planar_yuv8_buffer->DataU(), - cropped_planar_yuv8_buffer->StrideU(), - cropped_planar_yuv8_buffer->DataV(), - cropped_planar_yuv8_buffer->StrideV(), - nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), - nv12_buffer->MutableDataUV(), nv12_buffer->StrideUV(), - planar_yuv8_buffer->width(), - planar_yuv8_buffer->height()); + switch (video_frame_buffer_type) { + case VideoFrameBuffer::Type::kI420: + cropped_planar_yuv8_buffer = cropped_buffer->GetI420(); + libyuv::I420ToNV12(cropped_planar_yuv8_buffer->DataY(), + cropped_planar_yuv8_buffer->StrideY(), + cropped_planar_yuv8_buffer->DataU(), + cropped_planar_yuv8_buffer->StrideU(), + cropped_planar_yuv8_buffer->DataV(), + cropped_planar_yuv8_buffer->StrideV(), + nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), + nv12_buffer->MutableDataUV(), + nv12_buffer->StrideUV(), planar_yuv8_buffer->width(), + planar_yuv8_buffer->height()); + break; + case VideoFrameBuffer::Type::kI444: + cropped_planar_yuv8_buffer = cropped_buffer->GetI444(); + libyuv::I444ToNV12(cropped_planar_yuv8_buffer->DataY(), + cropped_planar_yuv8_buffer->StrideY(), + cropped_planar_yuv8_buffer->DataU(), + cropped_planar_yuv8_buffer->StrideU(), + cropped_planar_yuv8_buffer->DataV(), + cropped_planar_yuv8_buffer->StrideV(), + nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), + nv12_buffer->MutableDataUV(), + nv12_buffer->StrideUV(), planar_yuv8_buffer->width(), + planar_yuv8_buffer->height()); + break; + case VideoFrameBuffer::Type::kI422: + cropped_planar_yuv8_buffer = cropped_buffer->GetI422(); + // Swap src_u and src_v to implement I422ToNV12. + libyuv::I422ToNV21(cropped_planar_yuv8_buffer->DataY(), + cropped_planar_yuv8_buffer->StrideY(), + cropped_planar_yuv8_buffer->DataV(), + cropped_planar_yuv8_buffer->StrideV(), + cropped_planar_yuv8_buffer->DataU(), + cropped_planar_yuv8_buffer->StrideU(), + nv12_buffer->MutableDataY(), nv12_buffer->StrideY(), + nv12_buffer->MutableDataUV(), + nv12_buffer->StrideUV(), planar_yuv8_buffer->width(), + planar_yuv8_buffer->height()); + break; + default: + RTC_LOG(LS_ERROR) << "frame_buffer type: " + << static_cast(video_frame_buffer_type) + << " is not supported!"; + ReportError(); + return WEBRTC_VIDEO_CODEC_ERROR; } cropped_buffer = nv12_buffer; diff --git a/modules/video_coding/codecs/h264/include/h264.h b/modules/video_coding/codecs/h264/include/h264.h index 8c201d2b6a..ff7e16851b 100644 --- a/modules/video_coding/codecs/h264/include/h264.h +++ b/modules/video_coding/codecs/h264/include/h264.h @@ -16,8 +16,8 @@ #include #include -#include "absl/strings/string_view.h" #include "api/video_codecs/h264_profile_level_id.h" +#include "api/video_codecs/scalability_mode.h" #include "media/base/codec.h" #include "modules/video_coding/include/video_codec_interface.h" #include "rtc_base/system/rtc_export.h" @@ -52,7 +52,7 @@ class RTC_EXPORT H264Encoder : public VideoEncoder { static std::unique_ptr Create(const cricket::VideoCodec& codec); // If H.264 is supported (any implementation). static bool IsSupported(); - static bool SupportsScalabilityMode(absl::string_view scalability_mode); + static bool SupportsScalabilityMode(ScalabilityMode scalability_mode); ~H264Encoder() override {} }; diff --git a/modules/video_coding/codecs/test/android_codec_factory_helper.cc b/modules/video_coding/codecs/test/android_codec_factory_helper.cc index 1227b1fbbe..d1be684cbb 100644 --- a/modules/video_coding/codecs/test/android_codec_factory_helper.cc +++ b/modules/video_coding/codecs/test/android_codec_factory_helper.cc @@ -10,25 +10,19 @@ #include "modules/video_coding/codecs/test/android_codec_factory_helper.h" +#include #include +#include #include +#include "modules/utility/include/jvm_android.h" #include "rtc_base/checks.h" -#include "rtc_base/ignore_wundef.h" -#include "sdk/android/native_api/base/init.h" #include "sdk/android/native_api/codecs/wrapper.h" #include "sdk/android/native_api/jni/class_loader.h" #include "sdk/android/native_api/jni/jvm.h" #include "sdk/android/native_api/jni/scoped_java_ref.h" - -// Note: this dependency is dangerous since it reaches into Chromium's base. -// There's a risk of e.g. macro clashes. This file may only be used in tests. -// Since we use Chrome's build system for creating the gtest binary, this should -// be fine. -RTC_PUSH_IGNORING_WUNDEF() -#include "base/android/jni_android.h" -RTC_POP_IGNORING_WUNDEF() +#include "sdk/android/src/jni/jvm.h" namespace webrtc { namespace test { @@ -37,16 +31,15 @@ namespace { static pthread_once_t g_initialize_once = PTHREAD_ONCE_INIT; -// There can only be one JNI_OnLoad in each binary. So since this is a GTEST -// C++ runner binary, we want to initialize the same global objects we normally -// do if this had been a Java binary. void EnsureInitializedOnce() { - RTC_CHECK(::base::android::IsVMInitialized()); - JNIEnv* env = ::base::android::AttachCurrentThread(); - JavaVM* jvm = nullptr; - RTC_CHECK_EQ(0, env->GetJavaVM(&jvm)); + RTC_CHECK(::webrtc::jni::GetJVM() != nullptr); + + JNIEnv* jni = ::webrtc::jni::AttachCurrentThreadIfNeeded(); + JavaVM* jvm = NULL; + RTC_CHECK_EQ(0, jni->GetJavaVM(&jvm)); - InitAndroid(jvm); + // Initialize the Java environment (currently only used by the audio manager). + webrtc::JVM::Initialize(jvm); } } // namespace diff --git a/modules/video_coding/codecs/test/videocodec_test_av1.cc b/modules/video_coding/codecs/test/videocodec_test_av1.cc index af619e1f63..de8d70105f 100644 --- a/modules/video_coding/codecs/test/videocodec_test_av1.cc +++ b/modules/video_coding/codecs/test/videocodec_test_av1.cc @@ -52,7 +52,7 @@ TEST_P(VideoCodecTestAv1, HighBitrate) { auto config = CreateConfig("foreman_cif"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kCifWidth, kCifHeight); - config.codec_settings.SetScalabilityMode("NONE"); + config.codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); config.num_frames = kNumFramesLong; auto fixture = CreateVideoCodecTestFixture(config); @@ -61,7 +61,7 @@ TEST_P(VideoCodecTestAv1, HighBitrate) { std::vector rc_thresholds = { {12, 1, 0, 1, 0.3, 0.1, 0, 1}}; - std::vector quality_thresholds = {{37, 34, 0.94, 0.915}}; + std::vector quality_thresholds = {{37, 34, 0.94, 0.91}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } @@ -70,7 +70,7 @@ TEST_P(VideoCodecTestAv1, VeryLowBitrate) { auto config = CreateConfig("foreman_cif"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kCifWidth, kCifHeight); - config.codec_settings.SetScalabilityMode("NONE"); + config.codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); auto fixture = CreateVideoCodecTestFixture(config); std::vector rate_profiles = {{50, 30, 0}}; @@ -78,7 +78,7 @@ TEST_P(VideoCodecTestAv1, VeryLowBitrate) { std::vector rc_thresholds = { {15, 8, 75, 2, 2, 2, 2, 1}}; - std::vector quality_thresholds = {{28, 25, 0.70, 0.60}}; + std::vector quality_thresholds = {{28, 24.8, 0.70, 0.55}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } @@ -90,7 +90,7 @@ TEST_P(VideoCodecTestAv1, Hd) { auto config = CreateConfig("ConferenceMotion_1280_720_50"); config.SetCodecSettings(cricket::kAv1CodecName, 1, 1, 1, false, true, true, kHdWidth, kHdHeight); - config.codec_settings.SetScalabilityMode("NONE"); + config.codec_settings.SetScalabilityMode(ScalabilityMode::kL1T1); config.num_frames = kNumFramesLong; auto fixture = CreateVideoCodecTestFixture(config); @@ -99,7 +99,8 @@ TEST_P(VideoCodecTestAv1, Hd) { std::vector rc_thresholds = { {13, 3, 0, 1, 0.3, 0.1, 0, 1}}; - std::vector quality_thresholds = {{36, 31.6, 0.93, 0.87}}; + std::vector quality_thresholds = { + {35.9, 31.5, 0.925, 0.865}}; fixture->RunTest(rate_profiles, &rc_thresholds, &quality_thresholds, nullptr); } diff --git a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc index 871300a6e0..1029d901a0 100644 --- a/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc +++ b/modules/video_coding/codecs/test/videocodec_test_fixture_impl.cc @@ -104,7 +104,6 @@ std::string CodecSpecificToString(const VideoCodec& codec) { rtc::SimpleStringBuilder ss(buf); switch (codec.codecType) { case kVideoCodecVP8: - ss << "complexity: " << static_cast(codec.VP8().complexity); ss << "\nnum_temporal_layers: " << static_cast(codec.VP8().numberOfTemporalLayers); ss << "\ndenoising: " << codec.VP8().denoisingOn; @@ -113,7 +112,6 @@ std::string CodecSpecificToString(const VideoCodec& codec) { ss << "\nkey_frame_interval: " << codec.VP8().keyFrameInterval; break; case kVideoCodecVP9: - ss << "complexity: " << static_cast(codec.VP9().complexity); ss << "\nnum_temporal_layers: " << static_cast(codec.VP9().numberOfTemporalLayers); ss << "\nnum_spatial_layers: " @@ -302,6 +300,8 @@ std::string VideoCodecTestFixtureImpl::Config::ToString() const { ss << "\nnum_simulcast_streams: " << static_cast(codec_settings.numberOfSimulcastStreams); ss << "\n\n--> codec_settings." << codec_type; + ss << "complexity: " + << static_cast(codec_settings.GetVideoEncoderComplexity()); ss << "\n" << CodecSpecificToString(codec_settings); if (codec_settings.numberOfSimulcastStreams > 1) { for (int i = 0; i < codec_settings.numberOfSimulcastStreams; ++i) { diff --git a/modules/video_coding/codecs/test/videoprocessor.cc b/modules/video_coding/codecs/test/videoprocessor.cc index 09c0a75cc2..f5fd003185 100644 --- a/modules/video_coding/codecs/test/videoprocessor.cc +++ b/modules/video_coding/codecs/test/videoprocessor.cc @@ -507,7 +507,7 @@ void VideoProcessor::WriteDecodedFrame(const I420BufferInterface& decoded_frame, scaled_buffer = I420Buffer::Create(input_video_width, input_video_height); scaled_buffer->ScaleFrom(decoded_frame); - scaled_frame = scaled_buffer; + scaled_frame = scaled_buffer.get(); } // Ensure there is no padding. diff --git a/modules/video_coding/codecs/vp8/include/vp8.h b/modules/video_coding/codecs/vp8/include/vp8.h index 22f8de623d..d05c3a68d1 100644 --- a/modules/video_coding/codecs/vp8/include/vp8.h +++ b/modules/video_coding/codecs/vp8/include/vp8.h @@ -15,7 +15,6 @@ #include #include "absl/base/attributes.h" -#include "absl/strings/string_view.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" @@ -40,7 +39,6 @@ class VP8Encoder { static std::unique_ptr Create(); static std::unique_ptr Create(Settings settings); - static bool SupportsScalabilityMode(absl::string_view scalability_mode); ABSL_DEPRECATED("") static std::unique_ptr Create( diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc index a613be4154..24c85024f0 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc @@ -49,8 +49,6 @@ constexpr char kVP8IosMaxNumberOfThreadFieldTrial[] = constexpr char kVP8IosMaxNumberOfThreadFieldTrialParameter[] = "max_thread"; #endif -constexpr absl::string_view kSupportedScalabilityModes[] = {"L1T2", "L1T3"}; - constexpr char kVp8ForcePartitionResilience[] = "WebRTC-VP8-ForcePartitionResilience"; @@ -232,15 +230,6 @@ std::unique_ptr VP8Encoder::Create( std::move(settings)); } -bool VP8Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { - for (const auto& entry : kSupportedScalabilityModes) { - if (entry == scalability_mode) { - return true; - } - } - return false; -} - vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags( const Vp8FrameConfig& references) { RTC_DCHECK(!references.drop_frame); @@ -591,7 +580,7 @@ int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst, } // Allow the user to set the complexity for the base stream. - switch (inst->VP8().complexity) { + switch (inst->GetVideoEncoderComplexity()) { case VideoCodecComplexity::kComplexityHigh: cpu_speed_[0] = -5; break; @@ -1367,7 +1356,7 @@ LibvpxVp8Encoder::PrepareBuffers(rtc::scoped_refptr buffer) { // Prepare `raw_images_` from `mapped_buffer` and, if simulcast, scaled // versions of `buffer`. std::vector> prepared_buffers; - SetRawImagePlanes(&raw_images_[0], mapped_buffer); + SetRawImagePlanes(&raw_images_[0], mapped_buffer.get()); prepared_buffers.push_back(mapped_buffer); for (size_t i = 1; i < encoders_.size(); ++i) { // Native buffers should implement optimized scaling and is the preferred @@ -1410,7 +1399,7 @@ LibvpxVp8Encoder::PrepareBuffers(rtc::scoped_refptr buffer) { << VideoFrameBufferTypeToString(mapped_buffer->type()); return {}; } - SetRawImagePlanes(&raw_images_[i], scaled_buffer); + SetRawImagePlanes(&raw_images_[i], scaled_buffer.get()); prepared_buffers.push_back(scaled_buffer); } return prepared_buffers; diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc index c257edd7c1..0ec7a453c1 100644 --- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc +++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc @@ -76,7 +76,8 @@ class TestVp8Impl : public VideoCodecUnitTest { webrtc::test::CodecSettings(kVideoCodecVP8, codec_settings); codec_settings->width = kWidth; codec_settings->height = kHeight; - codec_settings->VP8()->complexity = VideoCodecComplexity::kComplexityNormal; + codec_settings->SetVideoEncoderComplexity( + VideoCodecComplexity::kComplexityNormal); } void EncodeAndWaitForFrame(const VideoFrame& input_frame, diff --git a/modules/video_coding/codecs/vp8/vp8_scalability.cc b/modules/video_coding/codecs/vp8/vp8_scalability.cc new file mode 100644 index 0000000000..df15be0127 --- /dev/null +++ b/modules/video_coding/codecs/vp8/vp8_scalability.cc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 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/video_coding/codecs/vp8/vp8_scalability.h" + +namespace webrtc { + +bool VP8SupportsScalabilityMode(ScalabilityMode scalability_mode) { + constexpr ScalabilityMode kSupportedScalabilityModes[] = { + ScalabilityMode::kL1T1, ScalabilityMode::kL1T2, ScalabilityMode::kL1T3}; + for (const auto& entry : kSupportedScalabilityModes) { + if (entry == scalability_mode) { + return true; + } + } + return false; +} + +} // namespace webrtc diff --git a/modules/video_coding/codecs/vp8/vp8_scalability.h b/modules/video_coding/codecs/vp8/vp8_scalability.h new file mode 100644 index 0000000000..11f018a564 --- /dev/null +++ b/modules/video_coding/codecs/vp8/vp8_scalability.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODING_CODECS_VP8_VP8_SCALABILITY_H_ +#define MODULES_VIDEO_CODING_CODECS_VP8_VP8_SCALABILITY_H_ + +#include "api/video_codecs/scalability_mode.h" + +namespace webrtc { + +bool VP8SupportsScalabilityMode(ScalabilityMode scalability_mode); + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_CODECS_VP8_VP8_SCALABILITY_H_ diff --git a/modules/video_coding/codecs/vp9/include/vp9.h b/modules/video_coding/codecs/vp9/include/vp9.h index 829680a806..9ef36b00ef 100644 --- a/modules/video_coding/codecs/vp9/include/vp9.h +++ b/modules/video_coding/codecs/vp9/include/vp9.h @@ -15,7 +15,7 @@ #include #include -#include "absl/strings/string_view.h" +#include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/sdp_video_format.h" #include "media/base/codec.h" #include "modules/video_coding/include/video_codec_interface.h" @@ -37,7 +37,7 @@ class VP9Encoder : public VideoEncoder { static std::unique_ptr Create(); // Parses VP9 Profile from `codec` and returns the appropriate implementation. static std::unique_ptr Create(const cricket::VideoCodec& codec); - static bool SupportsScalabilityMode(absl::string_view scalability_mode); + static bool SupportsScalabilityMode(ScalabilityMode scalability_mode); ~VP9Encoder() override {} }; diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc index 669dc55a4b..ffb4705e4f 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.cc @@ -96,7 +96,7 @@ ColorSpace ExtractVP9ColorSpace(vpx_color_space_t space_t, LibvpxVp9Decoder::LibvpxVp9Decoder() : LibvpxVp9Decoder(FieldTrialBasedConfig()) {} -LibvpxVp9Decoder::LibvpxVp9Decoder(const WebRtcKeyValueConfig& trials) +LibvpxVp9Decoder::LibvpxVp9Decoder(const FieldTrialsView& trials) : decode_complete_callback_(nullptr), inited_(false), decoder_(nullptr), diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h index e5636d848a..a680441f73 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h @@ -14,7 +14,7 @@ #ifdef RTC_ENABLE_VP9 -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/video_codecs/video_decoder.h" #include "common_video/include/video_frame_buffer_pool.h" #include "modules/video_coding/codecs/vp9/include/vp9.h" @@ -26,7 +26,7 @@ namespace webrtc { class LibvpxVp9Decoder : public VP9Decoder { public: LibvpxVp9Decoder(); - explicit LibvpxVp9Decoder(const WebRtcKeyValueConfig& trials); + explicit LibvpxVp9Decoder(const FieldTrialsView& trials); virtual ~LibvpxVp9Decoder(); diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc index 99680cbe79..3f9f4e9235 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.cc @@ -27,6 +27,7 @@ #include "common_video/libyuv/include/webrtc_libyuv.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "modules/video_coding/svc/scalable_video_controller.h" #include "modules/video_coding/svc/scalable_video_controller_no_layering.h" #include "modules/video_coding/svc/svc_rate_allocator.h" @@ -142,7 +143,14 @@ std::unique_ptr CreateVp9ScalabilityStructure( } } - auto scalability_structure_controller = CreateScalabilityStructure(name); + absl::optional scalability_mode = + ScalabilityModeFromString(name); + if (!scalability_mode.has_value()) { + RTC_LOG(LS_WARNING) << "Invalid scalability mode " << name; + return nullptr; + } + auto scalability_structure_controller = + CreateScalabilityStructure(*scalability_mode); if (scalability_structure_controller == nullptr) { RTC_LOG(LS_WARNING) << "Unsupported scalability structure " << name; } else { @@ -184,6 +192,17 @@ vpx_svc_ref_frame_config_t Vp9References( return ref_config; } +bool AllowDenoising() { + // Do not enable the denoiser on ARM since optimization is pending. + // Denoiser is on by default on other platforms. +#if !defined(WEBRTC_ARCH_ARM) && !defined(WEBRTC_ARCH_ARM64) && \ + !defined(ANDROID) + return true; +#else + return false; +#endif +} + } // namespace void LibvpxVp9Encoder::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt, @@ -194,7 +213,7 @@ void LibvpxVp9Encoder::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt, LibvpxVp9Encoder::LibvpxVp9Encoder(const cricket::VideoCodec& codec, std::unique_ptr interface, - const WebRtcKeyValueConfig& trials) + const FieldTrialsView& trials) : libvpx_(std::move(interface)), encoded_image_(), encoded_complete_callback_(nullptr), @@ -386,6 +405,15 @@ bool LibvpxVp9Encoder::SetSvcRates( } } + if (seen_active_layer && performance_flags_.use_per_layer_speed) { + bool denoiser_on = + AllowDenoising() && codec_.VP9()->denoisingOn && + performance_flags_by_spatial_index_[num_active_spatial_layers_ - 1] + .allow_denoising; + libvpx_->codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, + denoiser_on ? 1 : 0); + } + if (higher_layers_enabled && !force_key_frame_) { // Prohibit drop of all layers for the next frame, so newly enabled // layer would have a valid spatial reference. @@ -775,6 +803,10 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) { } } + UpdatePerformanceFlags(); + RTC_DCHECK_EQ(performance_flags_by_spatial_index_.size(), + static_cast(num_spatial_layers_)); + SvcRateAllocator init_allocator(codec_); current_bitrate_allocation_ = init_allocator.Allocate(VideoBitrateAllocationParameters( @@ -791,9 +823,6 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) { return WEBRTC_VIDEO_CODEC_UNINITIALIZED; } - UpdatePerformanceFlags(); - RTC_DCHECK_EQ(performance_flags_by_spatial_index_.size(), - static_cast(num_spatial_layers_)); if (performance_flags_.use_per_layer_speed) { for (int si = 0; si < num_spatial_layers_; ++si) { svc_params_.speed_per_layer[si] = @@ -801,6 +830,12 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) { svc_params_.loopfilter_ctrl[si] = performance_flags_by_spatial_index_[si].deblock_mode; } + bool denoiser_on = + AllowDenoising() && inst->VP9().denoisingOn && + performance_flags_by_spatial_index_[num_spatial_layers_ - 1] + .allow_denoising; + libvpx_->codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, + denoiser_on ? 1 : 0); } libvpx_->codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT, @@ -888,13 +923,10 @@ int LibvpxVp9Encoder::InitAndSetControlSettings(const VideoCodec* inst) { // Turn on row-based multithreading. libvpx_->codec_control(encoder_, VP9E_SET_ROW_MT, 1); -#if !defined(WEBRTC_ARCH_ARM) && !defined(WEBRTC_ARCH_ARM64) && \ - !defined(ANDROID) - // Do not enable the denoiser on ARM since optimization is pending. - // Denoiser is on by default on other platforms. - libvpx_->codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, - inst->VP9().denoisingOn ? 1 : 0); -#endif + if (AllowDenoising() && !performance_flags_.use_per_layer_speed) { + libvpx_->codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, + inst->VP9().denoisingOn ? 1 : 0); + } if (codec_.mode == VideoCodecMode::kScreensharing) { // Adjust internal parameters to screen content. @@ -1811,8 +1843,7 @@ size_t LibvpxVp9Encoder::SteadyStateSize(int sid, int tid) { // static LibvpxVp9Encoder::VariableFramerateExperiment -LibvpxVp9Encoder::ParseVariableFramerateConfig( - const WebRtcKeyValueConfig& trials) { +LibvpxVp9Encoder::ParseVariableFramerateConfig(const FieldTrialsView& trials) { FieldTrialFlag enabled = FieldTrialFlag("Enabled"); FieldTrialParameter framerate_limit("min_fps", 5.0); FieldTrialParameter qp("min_qp", 32); @@ -1834,7 +1865,7 @@ LibvpxVp9Encoder::ParseVariableFramerateConfig( // static LibvpxVp9Encoder::QualityScalerExperiment -LibvpxVp9Encoder::ParseQualityScalerConfig(const WebRtcKeyValueConfig& trials) { +LibvpxVp9Encoder::ParseQualityScalerConfig(const FieldTrialsView& trials) { FieldTrialFlag disabled = FieldTrialFlag("Disabled"); FieldTrialParameter low_qp("low_qp", kLowVp9QpThreshold); FieldTrialParameter high_qp("hihg_qp", kHighVp9QpThreshold); @@ -1851,14 +1882,30 @@ LibvpxVp9Encoder::ParseQualityScalerConfig(const WebRtcKeyValueConfig& trials) { } void LibvpxVp9Encoder::UpdatePerformanceFlags() { + flat_map params_by_resolution; + if (codec_.GetVideoEncoderComplexity() == + VideoCodecComplexity::kComplexityLow) { + // For low tier devices, always use speed 9. Only disable upper + // layer deblocking below QCIF. + params_by_resolution[0] = {.base_layer_speed = 9, + .high_layer_speed = 9, + .deblock_mode = 1, + .allow_denoising = true}; + params_by_resolution[352 * 288] = {.base_layer_speed = 9, + .high_layer_speed = 9, + .deblock_mode = 0, + .allow_denoising = true}; + } else { + params_by_resolution = performance_flags_.settings_by_resolution; + } + const auto find_speed = [&](int min_pixel_count) { - RTC_DCHECK(!performance_flags_.settings_by_resolution.empty()); - auto it = - performance_flags_.settings_by_resolution.upper_bound(min_pixel_count); + RTC_DCHECK(!params_by_resolution.empty()); + auto it = params_by_resolution.upper_bound(min_pixel_count); return std::prev(it)->second; }; - performance_flags_by_spatial_index_.clear(); + if (is_svc_) { for (int si = 0; si < num_spatial_layers_; ++si) { performance_flags_by_spatial_index_.push_back(find_speed( @@ -1873,7 +1920,7 @@ void LibvpxVp9Encoder::UpdatePerformanceFlags() { // static LibvpxVp9Encoder::PerformanceFlags LibvpxVp9Encoder::ParsePerformanceFlagsFromTrials( - const WebRtcKeyValueConfig& trials) { + const FieldTrialsView& trials) { struct Params : public PerformanceFlags::ParameterSet { int min_pixel_count = 0; }; @@ -1886,7 +1933,9 @@ LibvpxVp9Encoder::ParsePerformanceFlagsFromTrials( FieldTrialStructMember("base_layer_speed", [](Params* p) { return &p->base_layer_speed; }), FieldTrialStructMember("deblock_mode", - [](Params* p) { return &p->deblock_mode; })}, + [](Params* p) { return &p->deblock_mode; }), + FieldTrialStructMember("denoiser", + [](Params* p) { return &p->allow_denoising; })}, {}); FieldTrialFlag per_layer_speed("use_per_layer_speed"); @@ -1927,18 +1976,38 @@ LibvpxVp9Encoder::GetDefaultPerformanceFlags() { flags.use_per_layer_speed = true; #if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || defined(ANDROID) // Speed 8 on all layers for all resolutions. - flags.settings_by_resolution[0] = {8, 8, 0}; + flags.settings_by_resolution[0] = {.base_layer_speed = 8, + .high_layer_speed = 8, + .deblock_mode = 0, + .allow_denoising = true}; #else + // For smaller resolutions, use lower speed setting for the temporal base // layer (get some coding gain at the cost of increased encoding complexity). // Set encoder Speed 5 for TL0, encoder Speed 8 for upper temporal layers, and // disable deblocking for upper-most temporal layers. - flags.settings_by_resolution[0] = {5, 8, 1}; + flags.settings_by_resolution[0] = {.base_layer_speed = 5, + .high_layer_speed = 8, + .deblock_mode = 1, + .allow_denoising = true}; // Use speed 7 for QCIF and above. // Set encoder Speed 7 for TL0, encoder Speed 8 for upper temporal layers, and // enable deblocking for all temporal layers. - flags.settings_by_resolution[352 * 288] = {7, 8, 0}; + flags.settings_by_resolution[352 * 288] = {.base_layer_speed = 7, + .high_layer_speed = 8, + .deblock_mode = 0, + .allow_denoising = true}; + + // For very high resolution (1080p and up), turn the speed all the way up + // since this is very CPU intensive. Also disable denoising to save CPU, at + // these resolutions denoising appear less effective and hopefully you also + // have a less noisy video source at this point. + flags.settings_by_resolution[1920 * 1080] = {.base_layer_speed = 9, + .high_layer_speed = 9, + .deblock_mode = 0, + .allow_denoising = false}; + #endif return flags; } diff --git a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h index b5e9cc6d4b..6d0e5197f1 100644 --- a/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h +++ b/modules/video_coding/codecs/vp9/libvpx_vp9_encoder.h @@ -14,12 +14,11 @@ #ifdef RTC_ENABLE_VP9 -#include #include #include #include "api/fec_controller_override.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/vp9_profile.h" #include "common_video/include/video_frame_buffer_pool.h" @@ -28,6 +27,7 @@ #include "modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h" #include "modules/video_coding/svc/scalable_video_controller.h" #include "modules/video_coding/utility/framerate_controller_deprecated.h" +#include "rtc_base/containers/flat_map.h" #include "rtc_base/experiments/encoder_info_settings.h" #include "vpx/vp8cx.h" @@ -37,7 +37,7 @@ class LibvpxVp9Encoder : public VP9Encoder { public: LibvpxVp9Encoder(const cricket::VideoCodec& codec, std::unique_ptr interface, - const WebRtcKeyValueConfig& trials); + const FieldTrialsView& trials); ~LibvpxVp9Encoder() override; @@ -172,7 +172,7 @@ class LibvpxVp9Encoder : public VP9Encoder { size_t spatial_layer_id = 0; size_t temporal_layer_id = 0; }; - std::map ref_buf_; + flat_map ref_buf_; std::vector layer_frames_; // Variable frame-rate related fields and methods. @@ -190,7 +190,7 @@ class LibvpxVp9Encoder : public VP9Encoder { int frames_before_steady_state; } variable_framerate_experiment_; static VariableFramerateExperiment ParseVariableFramerateConfig( - const WebRtcKeyValueConfig& trials); + const FieldTrialsView& trials); FramerateControllerDeprecated variable_framerate_controller_; const struct QualityScalerExperiment { @@ -199,7 +199,7 @@ class LibvpxVp9Encoder : public VP9Encoder { bool enabled; } quality_scaler_experiment_; static QualityScalerExperiment ParseQualityScalerConfig( - const WebRtcKeyValueConfig& trials); + const FieldTrialsView& trials); const bool external_ref_ctrl_; // Flags that can affect speed vs quality tradeoff, and are configureable per @@ -220,11 +220,12 @@ class LibvpxVp9Encoder : public VP9Encoder { // 1 = disable deblock for top-most TL // 2 = disable deblock for all TLs int deblock_mode = 0; + bool allow_denoising = true; }; // Map from min pixel count to settings for that resolution and above. // E.g. if you want some settings A if below wvga (640x360) and some other // setting B at wvga and above, you'd use map {{0, A}, {230400, B}}. - std::map settings_by_resolution; + flat_map settings_by_resolution; }; // Performance flags, ordered by `min_pixel_count`. const PerformanceFlags performance_flags_; @@ -234,7 +235,7 @@ class LibvpxVp9Encoder : public VP9Encoder { performance_flags_by_spatial_index_; void UpdatePerformanceFlags(); static PerformanceFlags ParsePerformanceFlagsFromTrials( - const WebRtcKeyValueConfig& trials); + const FieldTrialsView& trials); static PerformanceFlags GetDefaultPerformanceFlags(); int num_steady_state_frames_; diff --git a/modules/video_coding/codecs/vp9/vp9.cc b/modules/video_coding/codecs/vp9/vp9.cc index 7b787f3e4f..8c0de92687 100644 --- a/modules/video_coding/codecs/vp9/vp9.cc +++ b/modules/video_coding/codecs/vp9/vp9.cc @@ -13,6 +13,7 @@ #include #include "api/transport/field_trial_based_config.h" +#include "api/video_codecs/scalability_mode.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/vp9_profile.h" #include "modules/video_coding/codecs/vp9/libvpx_vp9_decoder.h" @@ -24,11 +25,18 @@ namespace webrtc { namespace { -constexpr absl::string_view kSupportedScalabilityModes[] = { - "L1T2", "L1T3", "L2T1", "L2T2", "L2T3", "L3T1", - "L3T2", "L3T3", "L1T2h", "L1T3h", "L2T1h", "L2T2h", - "L2T3h", "L3T1h", "L3T2h", "L3T3h", "L2T2_KEY", "L2T3_KEY", - "L3T1_KEY", "L3T2_KEY", "L3T3_KEY"}; +constexpr ScalabilityMode kSupportedScalabilityModes[] = { + ScalabilityMode::kL1T2, ScalabilityMode::kL1T3, + ScalabilityMode::kL2T1, ScalabilityMode::kL2T2, + ScalabilityMode::kL2T3, ScalabilityMode::kL3T1, + ScalabilityMode::kL3T2, ScalabilityMode::kL3T3, + ScalabilityMode::kL1T2h, ScalabilityMode::kL1T3h, + ScalabilityMode::kL2T1h, ScalabilityMode::kL2T2h, + ScalabilityMode::kL2T3h, ScalabilityMode::kL3T1h, + ScalabilityMode::kL3T2h, ScalabilityMode::kL3T3h, + ScalabilityMode::kL2T2_KEY, ScalabilityMode::kL2T3_KEY, + ScalabilityMode::kL3T1_KEY, ScalabilityMode::kL3T2_KEY, + ScalabilityMode::kL3T3_KEY}; } // namespace std::vector SupportedVP9Codecs() { @@ -93,7 +101,7 @@ std::unique_ptr VP9Encoder::Create( #endif } -bool VP9Encoder::SupportsScalabilityMode(absl::string_view scalability_mode) { +bool VP9Encoder::SupportsScalabilityMode(ScalabilityMode scalability_mode) { for (const auto& entry : kSupportedScalabilityModes) { if (entry == scalability_mode) { return true; diff --git a/modules/video_coding/deprecated/BUILD.gn b/modules/video_coding/deprecated/BUILD.gn index a6fa790f9c..0155fc42d7 100644 --- a/modules/video_coding/deprecated/BUILD.gn +++ b/modules/video_coding/deprecated/BUILD.gn @@ -17,6 +17,7 @@ rtc_library("nack_module") { deps = [ "..:nack_requester", "../..:module_api", + "../../../api:field_trials_view", "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../rtc_base:checks", @@ -27,7 +28,6 @@ rtc_library("nack_module") { "../../../rtc_base/experiments:field_trial_parser", "../../../rtc_base/synchronization:mutex", "../../../system_wrappers", - "../../../system_wrappers:field_trial", "../../utility", ] absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] diff --git a/modules/video_coding/deprecated/nack_module.cc b/modules/video_coding/deprecated/nack_module.cc index 334eb821a2..0768bc48f8 100644 --- a/modules/video_coding/deprecated/nack_module.cc +++ b/modules/video_coding/deprecated/nack_module.cc @@ -18,7 +18,6 @@ #include "rtc_base/checks.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { @@ -33,10 +32,9 @@ const int kMaxReorderedPackets = 128; const int kNumReorderingBuckets = 10; const int kDefaultSendNackDelayMs = 0; -int64_t GetSendNackDelay() { +int64_t GetSendNackDelay(const FieldTrialsView& field_trials) { int64_t delay_ms = strtol( - webrtc::field_trial::FindFullName("WebRTC-SendNackDelayMs").c_str(), - nullptr, 10); + field_trials.Lookup("WebRTC-SendNackDelayMs").c_str(), nullptr, 10); if (delay_ms > 0 && delay_ms <= 20) { RTC_LOG(LS_INFO) << "SendNackDelay is set to " << delay_ms; return delay_ms; @@ -63,7 +61,8 @@ DEPRECATED_NackModule::BackoffSettings::BackoffSettings(TimeDelta min_retry, : min_retry_interval(min_retry), max_rtt(max_rtt), base(base) {} absl::optional -DEPRECATED_NackModule::BackoffSettings::ParseFromFieldTrials() { +DEPRECATED_NackModule::BackoffSettings::ParseFromFieldTrials( + const FieldTrialsView& field_trials) { // Matches magic number in RTPSender::OnReceivedNack(). const TimeDelta kDefaultMinRetryInterval = TimeDelta::Millis(5); // Upper bound on link-delay considered for exponential backoff. @@ -79,7 +78,7 @@ DEPRECATED_NackModule::BackoffSettings::ParseFromFieldTrials() { FieldTrialParameter max_rtt("max_rtt", kDefaultMaxRtt); FieldTrialParameter base("base", kDefaultBase); ParseFieldTrial({&enabled, &min_retry, &max_rtt, &base}, - field_trial::FindFullName("WebRTC-ExponentialNackBackoff")); + field_trials.Lookup("WebRTC-ExponentialNackBackoff")); if (enabled) { return DEPRECATED_NackModule::BackoffSettings(min_retry.Get(), @@ -91,7 +90,8 @@ DEPRECATED_NackModule::BackoffSettings::ParseFromFieldTrials() { DEPRECATED_NackModule::DEPRECATED_NackModule( Clock* clock, NackSender* nack_sender, - KeyFrameRequestSender* keyframe_request_sender) + KeyFrameRequestSender* keyframe_request_sender, + const FieldTrialsView& field_trials) : clock_(clock), nack_sender_(nack_sender), keyframe_request_sender_(keyframe_request_sender), @@ -100,8 +100,8 @@ DEPRECATED_NackModule::DEPRECATED_NackModule( rtt_ms_(kDefaultRttMs), newest_seq_num_(0), next_process_time_ms_(-1), - send_nack_delay_ms_(GetSendNackDelay()), - backoff_settings_(BackoffSettings::ParseFromFieldTrials()) { + send_nack_delay_ms_(GetSendNackDelay(field_trials)), + backoff_settings_(BackoffSettings::ParseFromFieldTrials(field_trials)) { RTC_DCHECK(clock_); RTC_DCHECK(nack_sender_); RTC_DCHECK(keyframe_request_sender_); diff --git a/modules/video_coding/deprecated/nack_module.h b/modules/video_coding/deprecated/nack_module.h index ec1a6889bc..3b49bd1e5a 100644 --- a/modules/video_coding/deprecated/nack_module.h +++ b/modules/video_coding/deprecated/nack_module.h @@ -18,6 +18,7 @@ #include #include "absl/base/attributes.h" +#include "api/field_trials_view.h" #include "api/units/time_delta.h" #include "modules/include/module.h" #include "modules/include/module_common_types.h" @@ -33,7 +34,8 @@ class DEPRECATED_NackModule : public Module { public: DEPRECATED_NackModule(Clock* clock, NackSender* nack_sender, - KeyFrameRequestSender* keyframe_request_sender); + KeyFrameRequestSender* keyframe_request_sender, + const FieldTrialsView& field_trials); int OnReceivedPacket(uint16_t seq_num, bool is_keyframe); int OnReceivedPacket(uint16_t seq_num, bool is_keyframe, bool is_recovered); @@ -69,7 +71,8 @@ class DEPRECATED_NackModule : public Module { struct BackoffSettings { BackoffSettings(TimeDelta min_retry, TimeDelta max_rtt, double base); - static absl::optional ParseFromFieldTrials(); + static absl::optional ParseFromFieldTrials( + const FieldTrialsView& field_trials); // Min time between nacks. const TimeDelta min_retry_interval; diff --git a/modules/video_coding/frame_buffer2.cc b/modules/video_coding/frame_buffer2.cc index b15db96364..421da753b8 100644 --- a/modules/video_coding/frame_buffer2.cc +++ b/modules/video_coding/frame_buffer2.cc @@ -19,6 +19,8 @@ #include #include "absl/container/inlined_vector.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" #include "api/video/encoded_image.h" #include "api/video/video_timing.h" #include "modules/video_coding/frame_helpers.h" @@ -31,7 +33,6 @@ #include "rtc_base/numerics/sequence_number_util.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace video_coding { @@ -56,13 +57,13 @@ constexpr int64_t kLogNonDecodedIntervalMs = 5000; FrameBuffer::FrameBuffer(Clock* clock, VCMTiming* timing, - VCMReceiveStatisticsCallback* stats_callback) + VCMReceiveStatisticsCallback* stats_callback, + const FieldTrialsView& field_trials) : decoded_frames_history_(kMaxFramesHistory), clock_(clock), callback_queue_(nullptr), - jitter_estimator_(clock), + jitter_estimator_(clock, field_trials), timing_(timing), - inter_frame_delay_(clock_->TimeInMilliseconds()), stopped_(false), protection_mode_(kProtectionNack), stats_callback_(stats_callback), @@ -72,7 +73,7 @@ FrameBuffer::FrameBuffer(Clock* clock, "max_decode_queue_size", kZeroPlayoutDelayDefaultMaxDecodeQueueSize) { ParseFieldTrial({&zero_playout_delay_max_decode_queue_size_}, - field_trial::FindFullName("WebRTC-ZeroPlayoutDelay")); + field_trials.Lookup("WebRTC-ZeroPlayoutDelay")); callback_checker_.Detach(); } @@ -104,7 +105,7 @@ void FrameBuffer::NextFrame(int64_t max_wait_time_ms, void FrameBuffer::StartWaitForNextFrameOnQueue() { RTC_DCHECK(callback_queue_); RTC_DCHECK(!callback_task_.Running()); - int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds()); + int64_t wait_ms = FindNextFrame(clock_->CurrentTime()); callback_task_ = RepeatingTaskHandle::DelayedStart( callback_queue_->Get(), TimeDelta::Millis(wait_ms), [this] { @@ -118,13 +119,12 @@ void FrameBuffer::StartWaitForNextFrameOnQueue() { if (!frames_to_decode_.empty()) { // We have frames, deliver! frame = GetNextFrame(); - timing_->SetLastDecodeScheduledTimestamp( - clock_->TimeInMilliseconds()); + timing_->SetLastDecodeScheduledTimestamp(clock_->CurrentTime()); } else if (clock_->TimeInMilliseconds() < latest_return_time_ms_) { // If there's no frames to decode and there is still time left, it // means that the frame buffer was cleared between creation and // execution of this task. Continue waiting for the remaining time. - int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds()); + int64_t wait_ms = FindNextFrame(clock_->CurrentTime()); return TimeDelta::Millis(wait_ms); } frame_handler = std::move(frame_handler_); @@ -137,8 +137,8 @@ void FrameBuffer::StartWaitForNextFrameOnQueue() { TaskQueueBase::DelayPrecision::kHigh); } -int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { - int64_t wait_ms = latest_return_time_ms_ - now_ms; +int64_t FrameBuffer::FindNextFrame(Timestamp now) { + int64_t wait_ms = latest_return_time_ms_ - now.ms(); frames_to_decode_.clear(); // `last_continuous_frame_` may be empty below, but nullopt is smaller @@ -217,14 +217,16 @@ int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { frames_to_decode_ = std::move(current_superframe); - if (frame->RenderTime() == -1) { - frame->SetRenderTime(timing_->RenderTimeMs(frame->Timestamp(), now_ms)); + absl::optional render_time = frame->RenderTimestamp(); + if (!render_time) { + render_time = timing_->RenderTime(frame->Timestamp(), now); + frame->SetRenderTime(render_time->ms()); } bool too_many_frames_queued = frames_.size() > zero_playout_delay_max_decode_queue_size_ ? true : false; - wait_ms = timing_->MaxWaitingTime(frame->RenderTime(), now_ms, - too_many_frames_queued); + wait_ms = + timing_->MaxWaitingTime(*render_time, now, too_many_frames_queued).ms(); // This will cause the frame buffer to prefer high framerate rather // than high resolution in the case of the decoder not decoding fast @@ -236,40 +238,40 @@ int64_t FrameBuffer::FindNextFrame(int64_t now_ms) { break; } - wait_ms = std::min(wait_ms, latest_return_time_ms_ - now_ms); + wait_ms = std::min(wait_ms, latest_return_time_ms_ - now.ms()); wait_ms = std::max(wait_ms, 0); return wait_ms; } std::unique_ptr FrameBuffer::GetNextFrame() { RTC_DCHECK_RUN_ON(&callback_checker_); - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); // TODO(ilnik): remove `frames_out` use frames_to_decode_ directly. std::vector> frames_out; RTC_DCHECK(!frames_to_decode_.empty()); bool superframe_delayed_by_retransmission = false; - size_t superframe_size = 0; + DataSize superframe_size = DataSize::Zero(); const EncodedFrame& first_frame = *frames_to_decode_[0]->second.frame; - int64_t render_time_ms = first_frame.RenderTime(); + absl::optional render_time = first_frame.RenderTimestamp(); int64_t receive_time_ms = first_frame.ReceivedTime(); // Gracefully handle bad RTP timestamps and render time issues. - if (FrameHasBadRenderTiming(first_frame.RenderTimeMs(), now_ms, - timing_->TargetVideoDelay())) { + if (!render_time || + FrameHasBadRenderTiming(*render_time, now, timing_->TargetVideoDelay())) { jitter_estimator_.Reset(); timing_->Reset(); - render_time_ms = timing_->RenderTimeMs(first_frame.Timestamp(), now_ms); + render_time = timing_->RenderTime(first_frame.Timestamp(), now); } for (FrameMap::iterator& frame_it : frames_to_decode_) { RTC_DCHECK(frame_it != frames_.end()); std::unique_ptr frame = std::move(frame_it->second.frame); - frame->SetRenderTime(render_time_ms); + frame->SetRenderTime(render_time->ms()); superframe_delayed_by_retransmission |= frame->delayed_by_retransmission(); receive_time_ms = std::max(receive_time_ms, frame->ReceivedTime()); - superframe_size += frame->size(); + superframe_size += DataSize::Bytes(frame->size()); PropagateDecodability(frame_it->second); decoded_frames_history_.InsertDecoded(frame_it->first, frame->Timestamp()); @@ -292,22 +294,23 @@ std::unique_ptr FrameBuffer::GetNextFrame() { } if (!superframe_delayed_by_retransmission) { - int64_t frame_delay; + auto frame_delay = inter_frame_delay_.CalculateDelay( + first_frame.Timestamp(), Timestamp::Millis(receive_time_ms)); - if (inter_frame_delay_.CalculateDelay(first_frame.Timestamp(), &frame_delay, - receive_time_ms)) { - jitter_estimator_.UpdateEstimate(frame_delay, superframe_size); + if (frame_delay) { + jitter_estimator_.UpdateEstimate(*frame_delay, superframe_size); } float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0; - absl::optional rtt_mult_add_cap_ms = absl::nullopt; + absl::optional rtt_mult_add_cap_ms = absl::nullopt; if (rtt_mult_settings_.has_value()) { rtt_mult = rtt_mult_settings_->rtt_mult_setting; - rtt_mult_add_cap_ms = rtt_mult_settings_->rtt_mult_add_cap_ms; + rtt_mult_add_cap_ms = + TimeDelta::Millis(rtt_mult_settings_->rtt_mult_add_cap_ms); } timing_->SetJitterDelay( jitter_estimator_.GetJitterEstimate(rtt_mult, rtt_mult_add_cap_ms)); - timing_->UpdateCurrentDelay(render_time_ms, now_ms); + timing_->UpdateCurrentDelay(*render_time, now); } else { if (RttMultExperiment::RttMultEnabled()) jitter_estimator_.FrameNacked(); @@ -351,7 +354,7 @@ int FrameBuffer::Size() { void FrameBuffer::UpdateRtt(int64_t rtt_ms) { MutexLock lock(&mutex_); - jitter_estimator_.UpdateRtt(rtt_ms); + jitter_estimator_.UpdateRtt(TimeDelta::Millis(rtt_ms)); } bool FrameBuffer::ValidReferences(const EncodedFrame& frame) const { @@ -446,8 +449,10 @@ int64_t FrameBuffer::InsertFrame(std::unique_ptr frame) { if (!UpdateFrameInfoWithIncomingFrame(*frame, info)) return last_continuous_frame_id; - if (!frame->delayed_by_retransmission()) - timing_->IncomingTimestamp(frame->Timestamp(), frame->ReceivedTime()); + // If ReceiveTime is negative then it is not a valid timestamp. + if (!frame->delayed_by_retransmission() && frame->ReceivedTime() >= 0) + timing_->IncomingTimestamp(frame->Timestamp(), + Timestamp::Millis(frame->ReceivedTime())); // It can happen that a frame will be reported as fully received even if a // lower spatial layer frame is missing. @@ -591,18 +596,12 @@ void FrameBuffer::UpdateJitterDelay() { if (!stats_callback_) return; - int max_decode_ms; - int current_delay_ms; - int target_delay_ms; - int jitter_buffer_ms; - int min_playout_delay_ms; - int render_delay_ms; - if (timing_->GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms, - &jitter_buffer_ms, &min_playout_delay_ms, - &render_delay_ms)) { + auto timings = timing_->GetTimings(); + if (timings.num_decoded_frames > 0) { stats_callback_->OnFrameBufferTimingsUpdated( - max_decode_ms, current_delay_ms, target_delay_ms, jitter_buffer_ms, - min_playout_delay_ms, render_delay_ms); + timings.max_decode_duration.ms(), timings.current_delay.ms(), + timings.target_delay.ms(), timings.jitter_buffer_delay.ms(), + timings.min_playout_delay.ms(), timings.render_delay.ms()); } } diff --git a/modules/video_coding/frame_buffer2.h b/modules/video_coding/frame_buffer2.h index f2e515c8ee..48aceab8db 100644 --- a/modules/video_coding/frame_buffer2.h +++ b/modules/video_coding/frame_buffer2.h @@ -18,6 +18,7 @@ #include #include "absl/container/inlined_vector.h" +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "api/video/encoded_frame.h" #include "modules/video_coding/include/video_coding_defines.h" @@ -47,7 +48,8 @@ class FrameBuffer { public: FrameBuffer(Clock* clock, VCMTiming* timing, - VCMReceiveStatisticsCallback* stats_callback); + VCMReceiveStatisticsCallback* stats_callback, + const FieldTrialsView& field_trials); FrameBuffer() = delete; FrameBuffer(const FrameBuffer&) = delete; @@ -118,7 +120,7 @@ class FrameBuffer { // Check that the references of `frame` are valid. bool ValidReferences(const EncodedFrame& frame) const; - int64_t FindNextFrame(int64_t now_ms) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + int64_t FindNextFrame(Timestamp now) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); std::unique_ptr GetNextFrame() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); diff --git a/modules/video_coding/frame_buffer2_unittest.cc b/modules/video_coding/frame_buffer2_unittest.cc index be838c748e..def2e2219e 100644 --- a/modules/video_coding/frame_buffer2_unittest.cc +++ b/modules/video_coding/frame_buffer2_unittest.cc @@ -16,6 +16,8 @@ #include #include +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/frame_object.h" #include "modules/video_coding/jitter_estimator.h" #include "modules/video_coding/timing.h" @@ -26,6 +28,7 @@ #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "test/time_controller/simulated_time_controller.h" using ::testing::_; @@ -38,58 +41,41 @@ namespace video_coding { class VCMTimingFake : public VCMTiming { public: - explicit VCMTimingFake(Clock* clock) : VCMTiming(clock) {} + explicit VCMTimingFake(Clock* clock, const FieldTrialsView& field_trials) + : VCMTiming(clock, field_trials) {} - int64_t RenderTimeMs(uint32_t frame_timestamp, - int64_t now_ms) const override { - if (last_ms_ == -1) { - last_ms_ = now_ms + kDelayMs; + Timestamp RenderTime(uint32_t frame_timestamp, Timestamp now) const override { + if (last_render_time_.IsMinusInfinity()) { + last_render_time_ = now + kDelay; last_timestamp_ = frame_timestamp; } - uint32_t diff = MinDiff(frame_timestamp, last_timestamp_); + auto diff = MinDiff(frame_timestamp, last_timestamp_); + auto timeDiff = TimeDelta::Millis(diff / 90); if (AheadOf(frame_timestamp, last_timestamp_)) - last_ms_ += diff / 90; + last_render_time_ += timeDiff; else - last_ms_ -= diff / 90; + last_render_time_ -= timeDiff; last_timestamp_ = frame_timestamp; - return last_ms_; + return last_render_time_; } - int64_t MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const override { - return render_time_ms - now_ms - kDecodeTime; + TimeDelta MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const override { + return render_time - now - kDecodeTime; } - bool GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const override { - return true; - } - - int GetCurrentJitter() { - int max_decode_ms; - int current_delay_ms; - int target_delay_ms; - int jitter_buffer_ms; - int min_playout_delay_ms; - int render_delay_ms; - VCMTiming::GetTimings(&max_decode_ms, ¤t_delay_ms, &target_delay_ms, - &jitter_buffer_ms, &min_playout_delay_ms, - &render_delay_ms); - return jitter_buffer_ms; + TimeDelta GetCurrentJitter() { + return VCMTiming::GetTimings().jitter_buffer_delay; } private: - static constexpr int kDelayMs = 50; - static constexpr int kDecodeTime = kDelayMs / 2; + static constexpr TimeDelta kDelay = TimeDelta::Millis(50); + const TimeDelta kDecodeTime = kDelay / 2; mutable uint32_t last_timestamp_ = 0; - mutable int64_t last_ms_ = -1; + mutable Timestamp last_render_time_ = Timestamp::MinusInfinity(); }; class FrameObjectFake : public EncodedFrame { @@ -120,12 +106,12 @@ class VCMReceiveStatisticsCallbackMock : public VCMReceiveStatisticsCallback { MOCK_METHOD(void, OnDroppedFrames, (uint32_t frames_dropped), (override)); MOCK_METHOD(void, OnFrameBufferTimingsUpdated, - (int max_decode_ms, - int current_delay_ms, - int target_delay_ms, - int jitter_buffer_ms, - int min_playout_delay_ms, - int render_delay_ms), + (int max_decode, + int current_delay, + int target_delay, + int jitter_buffer, + int min_playout_delay, + int render_delay), (override)); MOCK_METHOD(void, OnTimingFrameInfoUpdated, @@ -147,10 +133,11 @@ class TestFrameBuffer2 : public ::testing::Test { time_controller_.GetTaskQueueFactory()->CreateTaskQueue( "extract queue", TaskQueueFactory::Priority::NORMAL)), - timing_(time_controller_.GetClock()), + timing_(time_controller_.GetClock(), field_trials_), buffer_(new FrameBuffer(time_controller_.GetClock(), &timing_, - &stats_callback_)), + &stats_callback_, + field_trials_)), rand_(0x34678213) {} template @@ -229,6 +216,7 @@ class TestFrameBuffer2 : public ::testing::Test { uint32_t Rand() { return rand_.Rand(); } + test::ScopedKeyValueConfig field_trials_; webrtc::GlobalSimulatedTimeController time_controller_; rtc::TaskQueue time_task_queue_; VCMTimingFake timing_; @@ -292,9 +280,10 @@ TEST_F(TestFrameBuffer2, OneSuperFrame) { } TEST_F(TestFrameBuffer2, ZeroPlayoutDelay) { - VCMTiming timing(time_controller_.GetClock()); - buffer_.reset( - new FrameBuffer(time_controller_.GetClock(), &timing, &stats_callback_)); + test::ScopedKeyValueConfig field_trials; + VCMTiming timing(time_controller_.GetClock(), field_trials); + buffer_.reset(new FrameBuffer(time_controller_.GetClock(), &timing, + &stats_callback_, field_trials)); const VideoPlayoutDelay kPlayoutDelayMs = {0, 0}; std::unique_ptr test_frame(new FrameObjectFake()); test_frame->SetId(0); @@ -474,7 +463,7 @@ TEST_F(TestFrameBuffer2, ProtectionModeNackFEC) { ExtractFrame(); ExtractFrame(); ASSERT_EQ(4u, frames_.size()); - EXPECT_LT(timing_.GetCurrentJitter(), kRttMs); + EXPECT_LT(timing_.GetCurrentJitter().ms(), kRttMs); } TEST_F(TestFrameBuffer2, NoContinuousFrame) { @@ -537,6 +526,8 @@ TEST_F(TestFrameBuffer2, StatsCallback) { EXPECT_CALL(stats_callback_, OnCompleteFrame(true, kFrameSize, VideoContentType::UNSPECIFIED)); EXPECT_CALL(stats_callback_, OnFrameBufferTimingsUpdated(_, _, _, _, _, _)); + // Stats callback requires a previously decoded frame. + timing_.StopDecodeTimer(TimeDelta::Millis(1), Timestamp::Zero()); { std::unique_ptr frame(new FrameObjectFake()); diff --git a/modules/video_coding/frame_buffer3_unittest.cc b/modules/video_coding/frame_buffer3_unittest.cc deleted file mode 100644 index b70cd14d63..0000000000 --- a/modules/video_coding/frame_buffer3_unittest.cc +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 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/video_coding/frame_buffer3.h" - -#include - -#include "api/video/encoded_frame.h" -#include "test/field_trial.h" -#include "test/gmock.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -using ::testing::ElementsAre; -using ::testing::Eq; -using ::testing::IsEmpty; -using ::testing::Matches; - -MATCHER_P(FrameWithId, id, "") { - return Matches(Eq(id))(arg->Id()); -} - -class FakeEncodedFrame : public EncodedFrame { - public: - int64_t ReceivedTime() const override { return 0; } - int64_t RenderTime() const override { return 0; } -}; - -class Builder { - public: - Builder& Time(uint32_t rtp_timestamp) { - rtp_timestamp_ = rtp_timestamp; - return *this; - } - Builder& Id(int64_t frame_id) { - frame_id_ = frame_id; - return *this; - } - Builder& AsLast() { - last_spatial_layer_ = true; - return *this; - } - Builder& Refs(const std::vector& references) { - references_ = references; - return *this; - } - - std::unique_ptr Build() { - RTC_CHECK_LE(references_.size(), EncodedFrame::kMaxFrameReferences); - RTC_CHECK(rtp_timestamp_.has_value()); - RTC_CHECK(frame_id_.has_value()); - - auto frame = std::make_unique(); - frame->SetTimestamp(*rtp_timestamp_); - frame->SetId(*frame_id_); - frame->is_last_spatial_layer = last_spatial_layer_; - - for (int64_t ref : references_) { - frame->references[frame->num_references] = ref; - frame->num_references++; - } - - return frame; - } - - private: - absl::optional rtp_timestamp_; - absl::optional frame_id_; - bool last_spatial_layer_ = false; - std::vector references_; -}; - -TEST(FrameBuffer3Test, RejectInvalidRefs) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - // Ref must be less than the id of this frame. - buffer.InsertFrame(Builder().Time(0).Id(0).Refs({0}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); - - // Duplicate ids are also invalid. - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1, 1}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); -} - -TEST(FrameBuffer3Test, LastContinuousUpdatesOnInsertedFrames) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); - - buffer.InsertFrame(Builder().Time(10).Id(1).Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); - - buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(2)); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); -} - -TEST(FrameBuffer3Test, LastContinuousFrameReordering) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); - - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(3)); -} - -TEST(FrameBuffer3Test, LastContinuousTemporalUnit) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(10).Id(1).Build()); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); - buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); -} - -TEST(FrameBuffer3Test, LastContinuousTemporalUnitReordering) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(10).Id(1).Build()); - buffer.InsertFrame(Builder().Time(20).Id(3).Refs({1}).Build()); - buffer.InsertFrame(Builder().Time(20).Id(4).Refs({2, 3}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); - - buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(4)); -} - -TEST(FrameBuffer3Test, NextDecodable) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), - Eq(absl::nullopt)); - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); -} - -TEST(FrameBuffer3Test, AdvanceNextDecodableOnExtraction) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); - - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(1))); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(2))); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(30U)); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); -} - -TEST(FrameBuffer3Test, AdvanceLastDecodableOnExtraction) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(10U)); - - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(1))); - EXPECT_THAT(buffer.LastDecodableTemporalUnitRtpTimestamp(), Eq(30U)); -} - -TEST(FrameBuffer3Test, FrameUpdatesNextDecodable) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(20).Id(2).AsLast().Build()); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(20U)); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - EXPECT_THAT(buffer.NextDecodableTemporalUnitRtpTimestamp(), Eq(10U)); -} - -TEST(FrameBuffer3Test, KeyframeClearsFullBuffer) { - FrameBuffer buffer(/*max_frame_slots=*/5, /*max_decode_history=*/10); - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({2}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(40).Id(4).Refs({3}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(50).Id(5).Refs({4}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); - - // Frame buffer is full - buffer.InsertFrame(Builder().Time(60).Id(6).Refs({5}).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(5)); - - buffer.InsertFrame(Builder().Time(70).Id(7).AsLast().Build()); - EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(7)); -} - -TEST(FrameBuffer3Test, DropNextDecodableTemporalUnit) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); - - buffer.ExtractNextDecodableTemporalUnit(); - buffer.DropNextDecodableTemporalUnit(); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); -} - -TEST(FrameBuffer3Test, OldFramesAreIgnored) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - - buffer.ExtractNextDecodableTemporalUnit(); - buffer.ExtractNextDecodableTemporalUnit(); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); - - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); -} - -TEST(FrameBuffer3Test, ReturnFullTemporalUnitKSVC) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - buffer.InsertFrame(Builder().Time(10).Id(1).Build()); - buffer.InsertFrame(Builder().Time(10).Id(2).Refs({1}).Build()); - buffer.InsertFrame(Builder().Time(10).Id(3).Refs({2}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(1), FrameWithId(2), FrameWithId(3))); - - buffer.InsertFrame(Builder().Time(20).Id(4).Refs({3}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(4))); -} - -TEST(FrameBuffer3Test, InterleavedStream) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(30).Id(3).Refs({1}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(50).Id(5).Refs({3}).AsLast().Build()); - - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(1))); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(2))); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(4))); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(5))); - - buffer.InsertFrame(Builder().Time(70).Id(7).Refs({5}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(7))); - buffer.InsertFrame(Builder().Time(60).Id(6).Refs({4}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); - buffer.InsertFrame(Builder().Time(90).Id(9).Refs({7}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(9))); -} - -TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { - { - test::ScopedFieldTrials field_trial( - "WebRTC-LegacyFrameIdJumpBehavior/Disabled/"); - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); - buffer.InsertFrame(Builder().Time(30).Id(2).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); - } - - { - // WebRTC-LegacyFrameIdJumpBehavior is disabled by default. - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - - buffer.InsertFrame(Builder().Time(20).Id(3).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(3))); - buffer.InsertFrame(Builder().Time(30).Id(2).Refs({1}).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); - buffer.InsertFrame(Builder().Time(40).Id(1).AsLast().Build()); - EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), - ElementsAre(FrameWithId(1))); - } -} - -TEST(FrameBuffer3Test, TotalNumberOfContinuousTemporalUnits) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(0)); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); - - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); - EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); - - buffer.InsertFrame(Builder().Time(40).Id(4).Refs({2}).Build()); - buffer.InsertFrame(Builder().Time(40).Id(5).Refs({3, 4}).AsLast().Build()); - EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(1)); - - // Reordered - buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); - EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(3)); -} - -TEST(FrameBuffer3Test, TotalNumberOfDroppedFrames) { - FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100); - EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); - - buffer.InsertFrame(Builder().Time(10).Id(1).AsLast().Build()); - buffer.InsertFrame(Builder().Time(20).Id(2).Refs({1}).Build()); - buffer.InsertFrame(Builder().Time(20).Id(3).Refs({2}).AsLast().Build()); - buffer.InsertFrame(Builder().Time(40).Id(4).Refs({1}).Build()); - buffer.InsertFrame(Builder().Time(40).Id(5).Refs({4}).AsLast().Build()); - - buffer.ExtractNextDecodableTemporalUnit(); - EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); - - buffer.DropNextDecodableTemporalUnit(); - EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); - - buffer.ExtractNextDecodableTemporalUnit(); - EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(2)); -} - -} // namespace -} // namespace webrtc diff --git a/modules/video_coding/frame_helpers.cc b/modules/video_coding/frame_helpers.cc index 870f198dc4..08b47ef547 100644 --- a/modules/video_coding/frame_helpers.cc +++ b/modules/video_coding/frame_helpers.cc @@ -16,29 +16,29 @@ namespace webrtc { -bool FrameHasBadRenderTiming(int64_t render_time_ms, - int64_t now_ms, - int target_video_delay) { +bool FrameHasBadRenderTiming(Timestamp render_time, + Timestamp now, + TimeDelta target_video_delay) { // Zero render time means render immediately. - if (render_time_ms == 0) { + if (render_time.IsZero()) { return false; } - if (render_time_ms < 0) { + if (render_time < Timestamp::Zero()) { return true; } - const int64_t kMaxVideoDelayMs = 10000; - if (std::abs(render_time_ms - now_ms) > kMaxVideoDelayMs) { - int frame_delay = static_cast(std::abs(render_time_ms - now_ms)); + constexpr TimeDelta kMaxVideoDelay = TimeDelta::Millis(10000); + TimeDelta frame_delay = (render_time - now).Abs(); + if (frame_delay > kMaxVideoDelay) { RTC_LOG(LS_WARNING) << "A frame about to be decoded is out of the configured " "delay bounds (" - << frame_delay << " > " << kMaxVideoDelayMs + << frame_delay.ms() << " > " << kMaxVideoDelay.ms() << "). Resetting the video jitter buffer."; return true; } - if (target_video_delay > kMaxVideoDelayMs) { + if (target_video_delay > kMaxVideoDelay) { RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " - << kMaxVideoDelayMs << " ms."; + << kMaxVideoDelay.ms() << " ms."; return true; } return false; diff --git a/modules/video_coding/frame_helpers.h b/modules/video_coding/frame_helpers.h index e329d03706..b6d7b0f144 100644 --- a/modules/video_coding/frame_helpers.h +++ b/modules/video_coding/frame_helpers.h @@ -18,10 +18,9 @@ namespace webrtc { -// TODO(https://bugs.webrtc.org/13589): Switch to using Timestamp and TimeDelta. -bool FrameHasBadRenderTiming(int64_t render_time_ms, - int64_t now_ms, - int target_video_delay); +bool FrameHasBadRenderTiming(Timestamp render_time, + Timestamp now, + TimeDelta target_video_delay); std::unique_ptr CombineAndDeleteFrames( absl::InlinedVector, 4> frames); diff --git a/modules/video_coding/generic_decoder.cc b/modules/video_coding/generic_decoder.cc index fe59d5c7f1..7070fa2563 100644 --- a/modules/video_coding/generic_decoder.cc +++ b/modules/video_coding/generic_decoder.cc @@ -19,31 +19,19 @@ #include "modules/video_coding/include/video_error_codes.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { -VCMDecodedFrameCallback::VCMDecodedFrameCallback(VCMTiming* timing, - Clock* clock) - : _clock(clock), - _timing(timing), - _timestampMap(kDecoderFrameMemoryLength), - _extra_decode_time("t", absl::nullopt), - low_latency_renderer_enabled_("enabled", true), - low_latency_renderer_include_predecode_buffer_("include_predecode_buffer", - true) { +VCMDecodedFrameCallback::VCMDecodedFrameCallback( + VCMTiming* timing, + Clock* clock, + const FieldTrialsView& field_trials) + : _clock(clock), _timing(timing), _timestampMap(kDecoderFrameMemoryLength) { ntp_offset_ = _clock->CurrentNtpInMilliseconds() - _clock->TimeInMilliseconds(); - - ParseFieldTrial({&_extra_decode_time}, - field_trial::FindFullName("WebRTC-SlowDownDecoder")); - ParseFieldTrial({&low_latency_renderer_enabled_, - &low_latency_renderer_include_predecode_buffer_}, - field_trial::FindFullName("WebRTC-LowLatencyRenderer")); } VCMDecodedFrameCallback::~VCMDecodedFrameCallback() {} @@ -81,11 +69,6 @@ int32_t VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, absl::optional decode_time_ms, absl::optional qp) { - // Wait some extra time to simulate a slow decoder. - if (_extra_decode_time) { - rtc::Thread::SleepMs(_extra_decode_time->ms()); - } - RTC_DCHECK(_receiveCallback) << "Callback must not be null at this point"; TRACE_EVENT_INSTANT1("webrtc", "VCMDecodedFrameCallback::Decoded", "timestamp", decodedImage.timestamp()); @@ -122,19 +105,15 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, decodedImage.set_packet_infos(frameInfo->packet_infos); decodedImage.set_rotation(frameInfo->rotation); - if (low_latency_renderer_enabled_) { - absl::optional max_composition_delay_in_frames = - _timing->MaxCompositionDelayInFrames(); - if (max_composition_delay_in_frames) { - // Subtract frames that are in flight. - if (low_latency_renderer_include_predecode_buffer_) { - *max_composition_delay_in_frames -= timestamp_map_size; - *max_composition_delay_in_frames = - std::max(0, *max_composition_delay_in_frames); - } - decodedImage.set_max_composition_delay_in_frames( - max_composition_delay_in_frames); - } + absl::optional max_composition_delay_in_frames = + _timing->MaxCompositionDelayInFrames(); + if (max_composition_delay_in_frames) { + // Subtract frames that are in flight. + *max_composition_delay_in_frames -= timestamp_map_size; + *max_composition_delay_in_frames = + std::max(0, *max_composition_delay_in_frames); + decodedImage.set_max_composition_delay_in_frames( + max_composition_delay_in_frames); } RTC_DCHECK(frameInfo->decodeStart); @@ -142,7 +121,7 @@ void VCMDecodedFrameCallback::Decoded(VideoFrame& decodedImage, const TimeDelta decode_time = decode_time_ms ? TimeDelta::Millis(*decode_time_ms) : now - *frameInfo->decodeStart; - _timing->StopDecodeTimer(decode_time.ms(), now.ms()); + _timing->StopDecodeTimer(decode_time, now); decodedImage.set_processing_time( {*frameInfo->decodeStart, *frameInfo->decodeStart + decode_time}); diff --git a/modules/video_coding/generic_decoder.h b/modules/video_coding/generic_decoder.h index 31d8460194..efb77bbbd2 100644 --- a/modules/video_coding/generic_decoder.h +++ b/modules/video_coding/generic_decoder.h @@ -13,14 +13,13 @@ #include +#include "api/field_trials_view.h" #include "api/sequence_checker.h" -#include "api/units/time_delta.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/encoded_frame.h" #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/timestamp_map.h" #include "modules/video_coding/timing.h" -#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/synchronization/mutex.h" namespace webrtc { @@ -31,7 +30,9 @@ enum { kDecoderFrameMemoryLength = 10 }; class VCMDecodedFrameCallback : public DecodedImageCallback { public: - VCMDecodedFrameCallback(VCMTiming* timing, Clock* clock); + VCMDecodedFrameCallback(VCMTiming* timing, + Clock* clock, + const FieldTrialsView& field_trials); ~VCMDecodedFrameCallback() override; void SetUserReceiveCallback(VCMReceiveCallback* receiveCallback); VCMReceiveCallback* UserReceiveCallback(); @@ -61,18 +62,6 @@ class VCMDecodedFrameCallback : public DecodedImageCallback { Mutex lock_; VCMTimestampMap _timestampMap RTC_GUARDED_BY(lock_); int64_t ntp_offset_; - // Set by the field trial WebRTC-SlowDownDecoder to simulate a slow decoder. - FieldTrialOptional _extra_decode_time; - - // Set by the field trial WebRTC-LowLatencyRenderer. The parameter `enabled` - // determines if the low-latency renderer algorithm should be used for the - // case min playout delay=0 and max playout delay>0. - FieldTrialParameter low_latency_renderer_enabled_; - // Set by the field trial WebRTC-LowLatencyRenderer. The parameter - // `include_predecode_buffer` determines if the predecode buffer should be - // taken into account when calculating maximum number of frames in composition - // queue. - FieldTrialParameter low_latency_renderer_include_predecode_buffer_; }; class VCMGenericDecoder { diff --git a/modules/video_coding/generic_decoder_unittest.cc b/modules/video_coding/generic_decoder_unittest.cc index 466459ef95..d9ca8c6702 100644 --- a/modules/video_coding/generic_decoder_unittest.cc +++ b/modules/video_coding/generic_decoder_unittest.cc @@ -24,6 +24,7 @@ #include "test/fake_decoder.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace video_coding { @@ -66,10 +67,10 @@ class GenericDecoderTest : public ::testing::Test { protected: GenericDecoderTest() : clock_(0), - timing_(&clock_), + timing_(&clock_, field_trials_), task_queue_factory_(CreateDefaultTaskQueueFactory()), decoder_(task_queue_factory_.get()), - vcm_callback_(&timing_, &clock_), + vcm_callback_(&timing_, &clock_, field_trials_), generic_decoder_(&decoder_) {} void SetUp() override { @@ -82,6 +83,7 @@ class GenericDecoderTest : public ::testing::Test { generic_decoder_.Configure(settings); } + test::ScopedKeyValueConfig field_trials_; SimulatedClock clock_; VCMTiming timing_; std::unique_ptr task_queue_factory_; diff --git a/modules/video_coding/include/video_coding.h b/modules/video_coding/include/video_coding.h index 77b3eac236..80398fc2c1 100644 --- a/modules/video_coding/include/video_coding.h +++ b/modules/video_coding/include/video_coding.h @@ -11,6 +11,7 @@ #ifndef MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODING_H_ #define MODULES_VIDEO_CODING_INCLUDE_VIDEO_CODING_H_ +#include "api/field_trials_view.h" #include "api/video/video_frame.h" #include "api/video_codecs/video_decoder.h" #include "modules/include/module.h" @@ -28,7 +29,9 @@ struct CodecSpecificInfo; class VideoCodingModule : public Module { public: // DEPRECATED. - static VideoCodingModule* Create(Clock* clock); + static VideoCodingModule* Create( + Clock* clock, + const FieldTrialsView* field_trials = nullptr); /* * Receiver diff --git a/modules/video_coding/inter_frame_delay.cc b/modules/video_coding/inter_frame_delay.cc index d0c21aa771..8cdb6644ae 100644 --- a/modules/video_coding/inter_frame_delay.cc +++ b/modules/video_coding/inter_frame_delay.cc @@ -10,85 +10,62 @@ #include "modules/video_coding/inter_frame_delay.h" +#include "absl/types/optional.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { -VCMInterFrameDelay::VCMInterFrameDelay(int64_t currentWallClock) { - Reset(currentWallClock); +namespace { +constexpr Frequency k90kHz = Frequency::KiloHertz(90); +} + +VCMInterFrameDelay::VCMInterFrameDelay() { + Reset(); } // Resets the delay estimate. -void VCMInterFrameDelay::Reset(int64_t currentWallClock) { - _zeroWallClock = currentWallClock; - _wrapArounds = 0; - _prevWallClock = 0; - _prevTimestamp = 0; - _dTS = 0; +void VCMInterFrameDelay::Reset() { + prev_wall_clock_ = absl::nullopt; + prev_rtp_timestamp_unwrapped_ = 0; } // Calculates the delay of a frame with the given timestamp. // This method is called when the frame is complete. -bool VCMInterFrameDelay::CalculateDelay(uint32_t timestamp, - int64_t* delay, - int64_t currentWallClock) { - if (_prevWallClock == 0) { +absl::optional VCMInterFrameDelay::CalculateDelay( + uint32_t rtp_timestamp, + Timestamp now) { + int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp); + if (!prev_wall_clock_) { // First set of data, initialization, wait for next frame. - _prevWallClock = currentWallClock; - _prevTimestamp = timestamp; - *delay = 0; - return true; + prev_wall_clock_ = now; + prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped; + return TimeDelta::Zero(); } - int32_t prevWrapArounds = _wrapArounds; - CheckForWrapArounds(timestamp); - - // This will be -1 for backward wrap arounds and +1 for forward wrap arounds. - int32_t wrapAroundsSincePrev = _wrapArounds - prevWrapArounds; - // Account for reordering in jitter variance estimate in the future? // Note that this also captures incomplete frames which are grabbed for // decoding after a later frame has been complete, i.e. real packet losses. - if ((wrapAroundsSincePrev == 0 && timestamp < _prevTimestamp) || - wrapAroundsSincePrev < 0) { - *delay = 0; - return false; + uint32_t cropped_last = static_cast(prev_rtp_timestamp_unwrapped_); + if (rtp_timestamp_unwrapped < prev_rtp_timestamp_unwrapped_ || + !IsNewerTimestamp(rtp_timestamp, cropped_last)) { + return absl::nullopt; } - // Compute the compensated timestamp difference and convert it to ms and round - // it to closest integer. - _dTS = static_cast( - (timestamp + wrapAroundsSincePrev * (static_cast(1) << 32) - - _prevTimestamp) / - 90.0 + - 0.5); + // Compute the compensated timestamp difference. + int64_t d_rtp_ticks = rtp_timestamp_unwrapped - prev_rtp_timestamp_unwrapped_; + TimeDelta dts = d_rtp_ticks / k90kHz; + TimeDelta dt = now - *prev_wall_clock_; // frameDelay is the difference of dT and dTS -- i.e. the difference of the // wall clock time difference and the timestamp difference between two // following frames. - *delay = static_cast(currentWallClock - _prevWallClock - _dTS); - - _prevTimestamp = timestamp; - _prevWallClock = currentWallClock; + TimeDelta delay = dt - dts; - return true; + prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped; + prev_wall_clock_ = now; + return delay; } -// Investigates if the timestamp clock has overflowed since the last timestamp -// and keeps track of the number of wrap arounds since reset. -void VCMInterFrameDelay::CheckForWrapArounds(uint32_t timestamp) { - if (timestamp < _prevTimestamp) { - // This difference will probably be less than -2^31 if we have had a wrap - // around (e.g. timestamp = 1, _prevTimestamp = 2^32 - 1). Since it is cast - // to a int32_t, it should be positive. - if (static_cast(timestamp - _prevTimestamp) > 0) { - // Forward wrap around. - _wrapArounds++; - } - // This difference will probably be less than -2^31 if we have had a - // backward wrap around. Since it is cast to a int32_t, it should be - // positive. - } else if (static_cast(_prevTimestamp - timestamp) > 0) { - // Backward wrap around. - _wrapArounds--; - } -} } // namespace webrtc diff --git a/modules/video_coding/inter_frame_delay.h b/modules/video_coding/inter_frame_delay.h index f121c61498..e0fee6bfba 100644 --- a/modules/video_coding/inter_frame_delay.h +++ b/modules/video_coding/inter_frame_delay.h @@ -13,46 +13,32 @@ #include +#include "absl/types/optional.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { class VCMInterFrameDelay { public: - explicit VCMInterFrameDelay(int64_t currentWallClock); + VCMInterFrameDelay(); // Resets the estimate. Zeros are given as parameters. - void Reset(int64_t currentWallClock); + void Reset(); // Calculates the delay of a frame with the given timestamp. // This method is called when the frame is complete. - // - // Input: - // - timestamp : RTP timestamp of a received frame. - // - *delay : Pointer to memory where the result should be - // stored. - // - currentWallClock : The current time in milliseconds. - // Should be -1 for normal operation, only used - // for testing. - // Return value : true if OK, false when reordered timestamps. - bool CalculateDelay(uint32_t timestamp, - int64_t* delay, - int64_t currentWallClock); + absl::optional CalculateDelay(uint32_t rtp_timestamp, + Timestamp now); private: - // Controls if the RTP timestamp counter has had a wrap around between the - // current and the previously received frame. - // - // Input: - // - timestamp : RTP timestamp of the current frame. - void CheckForWrapArounds(uint32_t timestamp); - - int64_t _zeroWallClock; // Local timestamp of the first video packet received - int32_t _wrapArounds; // Number of wrapArounds detected - // The previous timestamp passed to the delay estimate - uint32_t _prevTimestamp; + // The previous rtp timestamp passed to the delay estimate + int64_t prev_rtp_timestamp_unwrapped_; + TimestampUnwrapper unwrapper_; + // The previous wall clock timestamp used by the delay estimate - int64_t _prevWallClock; - // Wrap-around compensated difference between incoming timestamps - int64_t _dTS; + absl::optional prev_wall_clock_; }; } // namespace webrtc diff --git a/modules/video_coding/inter_frame_delay_unittest.cc b/modules/video_coding/inter_frame_delay_unittest.cc new file mode 100644 index 0000000000..a338ba9d3b --- /dev/null +++ b/modules/video_coding/inter_frame_delay_unittest.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022 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/video_coding/inter_frame_delay.h" + +#include + +#include "absl/types/optional.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +// Test is for frames at 30fps. At 30fps, RTP timestamps will increase by +// 90000 / 30 = 3000 ticks per frame. +constexpr Frequency k30Fps = Frequency::Hertz(30); +constexpr TimeDelta kFrameDelay = 1 / k30Fps; +constexpr uint32_t kRtpTicksPerFrame = Frequency::KiloHertz(90) / k30Fps; +constexpr Timestamp kStartTime = Timestamp::Millis(1337); + +} // namespace + +using ::testing::Eq; +using ::testing::Optional; + +TEST(InterFrameDelayTest, OldRtpTimestamp) { + VCMInterFrameDelay inter_frame_delay; + EXPECT_THAT(inter_frame_delay.CalculateDelay(180000, kStartTime), + Optional(TimeDelta::Zero())); + EXPECT_THAT(inter_frame_delay.CalculateDelay(90000, kStartTime), + Eq(absl::nullopt)); +} + +TEST(InterFrameDelayTest, NegativeWrapAroundIsSameAsOldRtpTimestamp) { + VCMInterFrameDelay inter_frame_delay; + uint32_t rtp = 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime), + Optional(TimeDelta::Zero())); + // RTP has wrapped around backwards. + rtp -= 3000; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime), + Eq(absl::nullopt)); +} + +TEST(InterFrameDelayTest, CorrectDelayForFrames) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + + // First frame is always delay 0. + uint32_t rtp = 90000; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Perfectly timed frame has 0 delay. + clock.AdvanceTime(kFrameDelay); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Slightly early frame will have a negative delay. + clock.AdvanceTime(kFrameDelay - TimeDelta::Millis(3)); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-TimeDelta::Millis(3))); + + // Slightly late frame will have positive delay. + clock.AdvanceTime(kFrameDelay + TimeDelta::Micros(5125)); + rtp += kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Micros(5125))); + + // Simulate faster frame RTP at the same clock delay. The frame arrives late, + // since the RTP timestamp is faster than the delay, and thus is positive. + clock.AdvanceTime(kFrameDelay); + rtp += kRtpTicksPerFrame / 2; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(kFrameDelay / 2.0)); + + // Simulate slower frame RTP at the same clock delay. The frame is early, + // since the RTP timestamp advanced more than the delay, and thus is negative. + clock.AdvanceTime(kFrameDelay); + rtp += 1.5 * kRtpTicksPerFrame; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-kFrameDelay / 2.0)); +} + +TEST(InterFrameDelayTest, PositiveWrapAround) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + + // First frame is behind the max RTP by 1500. + uint32_t rtp = std::numeric_limits::max() - 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Rtp wraps around, now 1499. + rtp += kRtpTicksPerFrame; + + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay + TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Millis(1))); +} + +TEST(InterFrameDelayTest, MultipleWrapArounds) { + // Simulate a long pauses which cause wrap arounds multiple times. + constexpr Frequency k90Khz = Frequency::KiloHertz(90); + constexpr uint32_t kHalfRtp = std::numeric_limits::max() / 2; + constexpr TimeDelta kWrapAroundDelay = kHalfRtp / k90Khz; + + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + uint32_t rtp = 0; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + rtp += kHalfRtp; + clock.AdvanceTime(kWrapAroundDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + // 1st wrap around. + rtp += kHalfRtp + 1; + clock.AdvanceTime(kWrapAroundDelay + TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Millis(1) - (1 / k90Khz))); + + rtp += kHalfRtp; + clock.AdvanceTime(kWrapAroundDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + // 2nd wrap arounds. + rtp += kHalfRtp + 1; + clock.AdvanceTime(kWrapAroundDelay - TimeDelta::Millis(1)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-TimeDelta::Millis(1) - (1 / k90Khz))); + + // Ensure short delay (large RTP delay) between wrap-arounds has correct + // jitter. + rtp += kHalfRtp; + clock.AdvanceTime(TimeDelta::Millis(10)); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-(kWrapAroundDelay - TimeDelta::Millis(10)))); + // 3nd wrap arounds, this time with large RTP delay. + rtp += kHalfRtp + 1; + clock.AdvanceTime(TimeDelta::Millis(10)); + EXPECT_THAT( + inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(-(kWrapAroundDelay - TimeDelta::Millis(10) + (1 / k90Khz)))); +} + +TEST(InterFrameDelayTest, NegativeWrapAroundAfterPositiveWrapAround) { + VCMInterFrameDelay inter_frame_delay; + // Use a fake clock to simplify time keeping. + SimulatedClock clock(kStartTime); + uint32_t rtp = std::numeric_limits::max() - 1500; + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Rtp wraps around, now 1499. + rtp += kRtpTicksPerFrame; + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Optional(TimeDelta::Zero())); + + // Wrap back. + rtp -= kRtpTicksPerFrame; + // Frame delay should be as normal, in this case simulated as 1ms late. + clock.AdvanceTime(kFrameDelay); + EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()), + Eq(absl::nullopt)); +} + +} // namespace webrtc diff --git a/modules/video_coding/jitter_buffer.cc b/modules/video_coding/jitter_buffer.cc index 4c6015e027..6464b8c47b 100644 --- a/modules/video_coding/jitter_buffer.cc +++ b/modules/video_coding/jitter_buffer.cc @@ -9,11 +9,11 @@ */ #include "modules/video_coding/jitter_buffer.h" - #include #include #include +#include "api/units/timestamp.h" #include "modules/video_coding/frame_buffer.h" #include "modules/video_coding/include/video_coding.h" #include "modules/video_coding/inter_frame_delay.h" @@ -109,7 +109,8 @@ void FrameList::Reset(UnorderedFrameList* free_frames) { } VCMJitterBuffer::VCMJitterBuffer(Clock* clock, - std::unique_ptr event) + std::unique_ptr event, + const FieldTrialsView& field_trials) : clock_(clock), running_(false), frame_event_(std::move(event)), @@ -122,8 +123,7 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock, num_consecutive_old_packets_(0), num_packets_(0), num_duplicated_packets_(0), - jitter_estimate_(clock), - inter_frame_delay_(clock_->TimeInMilliseconds()), + jitter_estimate_(clock, field_trials), missing_sequence_numbers_(SequenceNumberLessThan()), latest_received_sequence_number_(0), max_nack_list_size_(0), @@ -192,7 +192,7 @@ void VCMJitterBuffer::Flush() { num_consecutive_old_packets_ = 0; // Also reset the jitter and delay estimates jitter_estimate_.Reset(); - inter_frame_delay_.Reset(clock_->TimeInMilliseconds()); + inter_frame_delay_.Reset(); waiting_for_completion_.frame_size = 0; waiting_for_completion_.timestamp = 0; waiting_for_completion_.latest_packet_time = -1; @@ -392,13 +392,13 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, if (error != kNoError) return error; - int64_t now_ms = clock_->TimeInMilliseconds(); + Timestamp now = clock_->CurrentTime(); // We are keeping track of the first and latest seq numbers, and // the number of wraps to be able to calculate how many packets we expect. if (first_packet_since_reset_) { // Now it's time to start estimating jitter // reset the delay estimate. - inter_frame_delay_.Reset(now_ms); + inter_frame_delay_.Reset(); } // Empty packets may bias the jitter estimate (lacking size component), @@ -408,9 +408,9 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, // This can get bad if we have a lot of duplicate packets, // we will then count some packet multiple times. waiting_for_completion_.frame_size += packet.sizeBytes; - waiting_for_completion_.latest_packet_time = now_ms; + waiting_for_completion_.latest_packet_time = now.ms(); } else if (waiting_for_completion_.latest_packet_time >= 0 && - waiting_for_completion_.latest_packet_time + 2000 <= now_ms) { + waiting_for_completion_.latest_packet_time + 2000 <= now.ms()) { // A packet should never be more than two seconds late UpdateJitterEstimate(waiting_for_completion_, true); waiting_for_completion_.latest_packet_time = -1; @@ -425,7 +425,7 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet, frame_data.rtt_ms = kDefaultRtt; frame_data.rolling_average_packets_per_frame = average_packets_per_frame_; VCMFrameBufferEnum buffer_state = - frame->InsertPacket(packet, now_ms, frame_data); + frame->InsertPacket(packet, now.ms(), frame_data); if (buffer_state > 0) { if (first_packet_since_reset_) { @@ -572,7 +572,7 @@ void VCMJitterBuffer::FindAndInsertContinuousFramesWithState( uint32_t VCMJitterBuffer::EstimatedJitterMs() { MutexLock lock(&mutex_); const double rtt_mult = 1.0f; - return jitter_estimate_.GetJitterEstimate(rtt_mult, absl::nullopt); + return jitter_estimate_.GetJitterEstimate(rtt_mult, absl::nullopt).ms(); } void VCMJitterBuffer::SetNackSettings(size_t max_nack_list_size, @@ -873,13 +873,15 @@ void VCMJitterBuffer::UpdateJitterEstimate(int64_t latest_packet_time_ms, if (latest_packet_time_ms == -1) { return; } - int64_t frame_delay; - bool not_reordered = inter_frame_delay_.CalculateDelay( - timestamp, &frame_delay, latest_packet_time_ms); + auto frame_delay = inter_frame_delay_.CalculateDelay( + timestamp, Timestamp::Millis(latest_packet_time_ms)); + + bool not_reordered = frame_delay.has_value(); // Filter out frames which have been reordered in time by the network if (not_reordered) { // Update the jitter estimate with the new samples - jitter_estimate_.UpdateEstimate(frame_delay, frame_size, incomplete_frame); + jitter_estimate_.UpdateEstimate(*frame_delay, DataSize::Bytes(frame_size), + incomplete_frame); } } diff --git a/modules/video_coding/jitter_buffer.h b/modules/video_coding/jitter_buffer.h index df7581a87e..72feffdcd4 100644 --- a/modules/video_coding/jitter_buffer.h +++ b/modules/video_coding/jitter_buffer.h @@ -17,6 +17,7 @@ #include #include +#include "api/field_trials_view.h" #include "modules/include/module_common_types.h" #include "modules/include/module_common_types_public.h" #include "modules/video_coding/decoding_state.h" @@ -69,7 +70,9 @@ class FrameList class VCMJitterBuffer { public: - VCMJitterBuffer(Clock* clock, std::unique_ptr event); + VCMJitterBuffer(Clock* clock, + std::unique_ptr event, + const FieldTrialsView& field_trials); ~VCMJitterBuffer(); diff --git a/modules/video_coding/jitter_buffer_unittest.cc b/modules/video_coding/jitter_buffer_unittest.cc index 801eeb6754..930eca5d91 100644 --- a/modules/video_coding/jitter_buffer_unittest.cc +++ b/modules/video_coding/jitter_buffer_unittest.cc @@ -23,11 +23,10 @@ #include "modules/video_coding/test/stream_generator.h" #include "rtc_base/location.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -37,7 +36,7 @@ class TestBasicJitterBuffer : public ::testing::Test { void SetUp() override { clock_.reset(new SimulatedClock(0)); jitter_buffer_.reset(new VCMJitterBuffer( - clock_.get(), absl::WrapUnique(EventWrapper::Create()))); + clock_.get(), absl::WrapUnique(EventWrapper::Create()), field_trials_)); jitter_buffer_->Start(); seq_num_ = 1234; timestamp_ = 0; @@ -118,6 +117,7 @@ class TestBasicJitterBuffer : public ::testing::Test { uint32_t timestamp_; int size_; uint8_t data_[1500]; + test::ScopedKeyValueConfig field_trials_; std::unique_ptr packet_; std::unique_ptr clock_; std::unique_ptr jitter_buffer_; @@ -132,7 +132,7 @@ class TestRunningJitterBuffer : public ::testing::Test { max_nack_list_size_ = 150; oldest_packet_to_nack_ = 250; jitter_buffer_ = new VCMJitterBuffer( - clock_.get(), absl::WrapUnique(EventWrapper::Create())); + clock_.get(), absl::WrapUnique(EventWrapper::Create()), field_trials_); stream_generator_ = new StreamGenerator(0, clock_->TimeInMilliseconds()); jitter_buffer_->Start(); jitter_buffer_->SetNackSettings(max_nack_list_size_, oldest_packet_to_nack_, @@ -212,6 +212,7 @@ class TestRunningJitterBuffer : public ::testing::Test { return ret; } + test::ScopedKeyValueConfig field_trials_; VCMJitterBuffer* jitter_buffer_; StreamGenerator* stream_generator_; std::unique_ptr clock_; diff --git a/modules/video_coding/jitter_estimator.cc b/modules/video_coding/jitter_estimator.cc index 87848aec06..acc36a94dd 100644 --- a/modules/video_coding/jitter_estimator.cc +++ b/modules/video_coding/jitter_estimator.cc @@ -17,198 +17,178 @@ #include #include "absl/types/optional.h" -#include "modules/video_coding/internal_defines.h" +#include "api/field_trials_view.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/rtt_filter.h" #include "rtc_base/experiments/jitter_upper_bound_experiment.h" #include "rtc_base/numerics/safe_conversions.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { static constexpr uint32_t kStartupDelaySamples = 30; static constexpr int64_t kFsAccuStartupSamples = 5; -static constexpr double kMaxFramerateEstimate = 200.0; -static constexpr int64_t kNackCountTimeoutMs = 60000; +static constexpr Frequency kMaxFramerateEstimate = Frequency::Hertz(200); +static constexpr TimeDelta kNackCountTimeout = TimeDelta::Seconds(60); static constexpr double kDefaultMaxTimestampDeviationInSigmas = 3.5; + +constexpr double kPhi = 0.97; +constexpr double kPsi = 0.9999; +constexpr uint32_t kAlphaCountMax = 400; +constexpr double kThetaLow = 0.000001; +constexpr uint32_t kNackLimit = 3; +constexpr int32_t kNumStdDevDelayOutlier = 15; +constexpr int32_t kNumStdDevFrameSizeOutlier = 3; +// ~Less than 1% chance (look up in normal distribution table)... +constexpr double kNoiseStdDevs = 2.33; +// ...of getting 30 ms freezes +constexpr double kNoiseStdDevOffset = 30.0; + } // namespace -VCMJitterEstimator::VCMJitterEstimator(Clock* clock) - : _phi(0.97), - _psi(0.9999), - _alphaCountMax(400), - _thetaLow(0.000001), - _nackLimit(3), - _numStdDevDelayOutlier(15), - _numStdDevFrameSizeOutlier(3), - _noiseStdDevs(2.33), // ~Less than 1% chance - // (look up in normal distribution table)... - _noiseStdDevOffset(30.0), // ...of getting 30 ms freezes - _rttFilter(), - fps_counter_(30), // TODO(sprang): Use an estimator with limit based on +VCMJitterEstimator::VCMJitterEstimator(Clock* clock, + const FieldTrialsView& field_trials) + : fps_counter_(30), // TODO(sprang): Use an estimator with limit based on // time, rather than number of samples. time_deviation_upper_bound_( JitterUpperBoundExperiment::GetUpperBoundSigmas().value_or( kDefaultMaxTimestampDeviationInSigmas)), enable_reduced_delay_( - !field_trial::IsEnabled("WebRTC-ReducedJitterDelayKillSwitch")), + !field_trials.IsEnabled("WebRTC-ReducedJitterDelayKillSwitch")), clock_(clock) { Reset(); } -VCMJitterEstimator::~VCMJitterEstimator() {} - -VCMJitterEstimator& VCMJitterEstimator::operator=( - const VCMJitterEstimator& rhs) { - if (this != &rhs) { - memcpy(_thetaCov, rhs._thetaCov, sizeof(_thetaCov)); - memcpy(_Qcov, rhs._Qcov, sizeof(_Qcov)); - - _avgFrameSize = rhs._avgFrameSize; - _varFrameSize = rhs._varFrameSize; - _maxFrameSize = rhs._maxFrameSize; - _fsSum = rhs._fsSum; - _fsCount = rhs._fsCount; - _lastUpdateT = rhs._lastUpdateT; - _prevEstimate = rhs._prevEstimate; - _prevFrameSize = rhs._prevFrameSize; - _avgNoise = rhs._avgNoise; - _alphaCount = rhs._alphaCount; - _filterJitterEstimate = rhs._filterJitterEstimate; - _startupCount = rhs._startupCount; - _latestNackTimestamp = rhs._latestNackTimestamp; - _nackCount = rhs._nackCount; - _rttFilter = rhs._rttFilter; - clock_ = rhs.clock_; - } - return *this; -} +VCMJitterEstimator::~VCMJitterEstimator() = default; // Resets the JitterEstimate. void VCMJitterEstimator::Reset() { - _theta[0] = 1 / (512e3 / 8); - _theta[1] = 0; - _varNoise = 4.0; - - _thetaCov[0][0] = 1e-4; - _thetaCov[1][1] = 1e2; - _thetaCov[0][1] = _thetaCov[1][0] = 0; - _Qcov[0][0] = 2.5e-10; - _Qcov[1][1] = 1e-10; - _Qcov[0][1] = _Qcov[1][0] = 0; - _avgFrameSize = 500; - _maxFrameSize = 500; - _varFrameSize = 100; - _lastUpdateT = -1; - _prevEstimate = -1.0; - _prevFrameSize = 0; - _avgNoise = 0.0; - _alphaCount = 1; - _filterJitterEstimate = 0.0; - _latestNackTimestamp = 0; - _nackCount = 0; - _latestNackTimestamp = 0; - _fsSum = 0; - _fsCount = 0; - _startupCount = 0; - _rttFilter.Reset(); + theta_[0] = 1 / (512e3 / 8); + theta_[1] = 0; + var_noise_ = 4.0; + + theta_cov_[0][0] = 1e-4; + theta_cov_[1][1] = 1e2; + theta_cov_[0][1] = theta_cov_[1][0] = 0; + q_cov_[0][0] = 2.5e-10; + q_cov_[1][1] = 1e-10; + q_cov_[0][1] = q_cov_[1][0] = 0; + avg_frame_size_ = kDefaultAvgAndMaxFrameSize; + max_frame_size_ = kDefaultAvgAndMaxFrameSize; + var_frame_size_ = 100; + last_update_time_ = absl::nullopt; + prev_estimate_ = absl::nullopt; + prev_frame_size_ = absl::nullopt; + avg_noise_ = 0.0; + alpha_count_ = 1; + filter_jitter_estimate_ = TimeDelta::Zero(); + latest_nack_ = Timestamp::Zero(); + nack_count_ = 0; + frame_size_sum_ = DataSize::Zero(); + frame_size_count_ = 0; + startup_count_ = 0; + rtt_filter_.Reset(); fps_counter_.Reset(); } // Updates the estimates with the new measurements. -void VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, - uint32_t frameSizeBytes, - bool incompleteFrame /* = false */) { - if (frameSizeBytes == 0) { +void VCMJitterEstimator::UpdateEstimate(TimeDelta frame_delay, + DataSize frame_size, + bool incomplete_frame /* = false */) { + if (frame_size.IsZero()) { return; } - int deltaFS = frameSizeBytes - _prevFrameSize; - if (_fsCount < kFsAccuStartupSamples) { - _fsSum += frameSizeBytes; - _fsCount++; - } else if (_fsCount == kFsAccuStartupSamples) { + // Can't use DataSize since this can be negative. + double delta_frame_bytes = + frame_size.bytes() - prev_frame_size_.value_or(DataSize::Zero()).bytes(); + if (frame_size_count_ < kFsAccuStartupSamples) { + frame_size_sum_ += frame_size; + frame_size_count_++; + } else if (frame_size_count_ == kFsAccuStartupSamples) { // Give the frame size filter. - _avgFrameSize = static_cast(_fsSum) / static_cast(_fsCount); - _fsCount++; + avg_frame_size_ = frame_size_sum_ / static_cast(frame_size_count_); + frame_size_count_++; } - if (!incompleteFrame || frameSizeBytes > _avgFrameSize) { - double avgFrameSize = _phi * _avgFrameSize + (1 - _phi) * frameSizeBytes; - if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize)) { + if (!incomplete_frame || frame_size > avg_frame_size_) { + DataSize avg_frame_size = kPhi * avg_frame_size_ + (1 - kPhi) * frame_size; + DataSize deviation_size = DataSize::Bytes(2 * sqrt(var_frame_size_)); + if (frame_size < avg_frame_size_ + deviation_size) { // Only update the average frame size if this sample wasn't a key frame. - _avgFrameSize = avgFrameSize; + avg_frame_size_ = avg_frame_size; } // Update the variance anyway since we want to capture cases where we only // get key frames. - _varFrameSize = VCM_MAX( - _phi * _varFrameSize + (1 - _phi) * (frameSizeBytes - avgFrameSize) * - (frameSizeBytes - avgFrameSize), - 1.0); + double delta_bytes = frame_size.bytes() - avg_frame_size.bytes(); + var_frame_size_ = std::max( + kPhi * var_frame_size_ + (1 - kPhi) * (delta_bytes * delta_bytes), 1.0); } - // Update max frameSize estimate. - _maxFrameSize = - VCM_MAX(_psi * _maxFrameSize, static_cast(frameSizeBytes)); + // Update max_frame_size_ estimate. + max_frame_size_ = std::max(kPsi * max_frame_size_, frame_size); - if (_prevFrameSize == 0) { - _prevFrameSize = frameSizeBytes; + if (!prev_frame_size_) { + prev_frame_size_ = frame_size; return; } - _prevFrameSize = frameSizeBytes; + prev_frame_size_ = frame_size; - // Cap frameDelayMS based on the current time deviation noise. - int64_t max_time_deviation_ms = - static_cast(time_deviation_upper_bound_ * sqrt(_varNoise) + 0.5); - frameDelayMS = std::max(std::min(frameDelayMS, max_time_deviation_ms), - -max_time_deviation_ms); + // Cap frame_delay based on the current time deviation noise. + TimeDelta max_time_deviation = + TimeDelta::Millis(time_deviation_upper_bound_ * sqrt(var_noise_) + 0.5); + frame_delay.Clamp(-max_time_deviation, max_time_deviation); // Only update the Kalman filter if the sample is not considered an extreme // outlier. Even if it is an extreme outlier from a delay point of view, if // the frame size also is large the deviation is probably due to an incorrect // line slope. - double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS); + double deviation = DeviationFromExpectedDelay(frame_delay, delta_frame_bytes); - if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) || - frameSizeBytes > - _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize)) { + if (fabs(deviation) < kNumStdDevDelayOutlier * sqrt(var_noise_) || + frame_size.bytes() > + avg_frame_size_.bytes() + + kNumStdDevFrameSizeOutlier * sqrt(var_frame_size_)) { // Update the variance of the deviation from the line given by the Kalman // filter. - EstimateRandomJitter(deviation, incompleteFrame); + EstimateRandomJitter(deviation, incomplete_frame); // Prevent updating with frames which have been congested by a large frame, // and therefore arrives almost at the same time as that frame. // This can occur when we receive a large frame (key frame) which has been // delayed. The next frame is of normal size (delta frame), and thus deltaFS // will be << 0. This removes all frame samples which arrives after a key // frame. - if ((!incompleteFrame || deviation >= 0.0) && - static_cast(deltaFS) > -0.25 * _maxFrameSize) { + if ((!incomplete_frame || deviation >= 0) && + delta_frame_bytes > -0.25 * max_frame_size_.bytes()) { // Update the Kalman filter with the new data - KalmanEstimateChannel(frameDelayMS, deltaFS); + KalmanEstimateChannel(frame_delay, delta_frame_bytes); } } else { int nStdDev = - (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier; - EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame); + (deviation >= 0) ? kNumStdDevDelayOutlier : -kNumStdDevDelayOutlier; + EstimateRandomJitter(nStdDev * sqrt(var_noise_), incomplete_frame); } // Post process the total estimated jitter - if (_startupCount >= kStartupDelaySamples) { + if (startup_count_ >= kStartupDelaySamples) { PostProcessEstimate(); } else { - _startupCount++; + startup_count_++; } } // Updates the nack/packet ratio. void VCMJitterEstimator::FrameNacked() { - if (_nackCount < _nackLimit) { - _nackCount++; + if (nack_count_ < kNackLimit) { + nack_count_++; } - _latestNackTimestamp = clock_->TimeInMicroseconds(); + latest_nack_ = clock_->CurrentTime(); } // Updates Kalman estimate of the channel. // The caller is expected to sanity check the inputs. -void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, - int32_t deltaFSBytes) { +void VCMJitterEstimator::KalmanEstimateChannel(TimeDelta frame_delay, + double delta_frame_size_bytes) { double Mh[2]; double hMh_sigma; double kalmanGain[2]; @@ -219,31 +199,31 @@ void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, // Prediction // M = M + Q - _thetaCov[0][0] += _Qcov[0][0]; - _thetaCov[0][1] += _Qcov[0][1]; - _thetaCov[1][0] += _Qcov[1][0]; - _thetaCov[1][1] += _Qcov[1][1]; + theta_cov_[0][0] += q_cov_[0][0]; + theta_cov_[0][1] += q_cov_[0][1]; + theta_cov_[1][0] += q_cov_[1][0]; + theta_cov_[1][1] += q_cov_[1][1]; // Kalman gain // K = M*h'/(sigma2n + h*M*h') = M*h'/(1 + h*M*h') // h = [dFS 1] // Mh = M*h' // hMh_sigma = h*M*h' + R - Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1]; - Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1]; + Mh[0] = theta_cov_[0][0] * delta_frame_size_bytes + theta_cov_[0][1]; + Mh[1] = theta_cov_[1][0] * delta_frame_size_bytes + theta_cov_[1][1]; // sigma weights measurements with a small deltaFS as noisy and // measurements with large deltaFS as good - if (_maxFrameSize < 1.0) { + if (max_frame_size_ < DataSize::Bytes(1)) { return; } - double sigma = (300.0 * exp(-fabs(static_cast(deltaFSBytes)) / - (1e0 * _maxFrameSize)) + + double sigma = (300.0 * exp(-fabs(delta_frame_size_bytes) / + (1e0 * max_frame_size_.bytes())) + 1) * - sqrt(_varNoise); + sqrt(var_noise_); if (sigma < 1.0) { sigma = 1.0; } - hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma; + hMh_sigma = delta_frame_size_bytes * Mh[0] + Mh[1] + sigma; if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0)) { RTC_DCHECK_NOTREACHED(); @@ -254,94 +234,96 @@ void VCMJitterEstimator::KalmanEstimateChannel(int64_t frameDelayMS, // Correction // theta = theta + K*(dT - h*theta) - measureRes = frameDelayMS - (deltaFSBytes * _theta[0] + _theta[1]); - _theta[0] += kalmanGain[0] * measureRes; - _theta[1] += kalmanGain[1] * measureRes; + measureRes = + frame_delay.ms() - (delta_frame_size_bytes * theta_[0] + theta_[1]); + theta_[0] += kalmanGain[0] * measureRes; + theta_[1] += kalmanGain[1] * measureRes; - if (_theta[0] < _thetaLow) { - _theta[0] = _thetaLow; + if (theta_[0] < kThetaLow) { + theta_[0] = kThetaLow; } // M = (I - K*h)*M - t00 = _thetaCov[0][0]; - t01 = _thetaCov[0][1]; - _thetaCov[0][0] = (1 - kalmanGain[0] * deltaFSBytes) * t00 - - kalmanGain[0] * _thetaCov[1][0]; - _thetaCov[0][1] = (1 - kalmanGain[0] * deltaFSBytes) * t01 - - kalmanGain[0] * _thetaCov[1][1]; - _thetaCov[1][0] = _thetaCov[1][0] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t00; - _thetaCov[1][1] = _thetaCov[1][1] * (1 - kalmanGain[1]) - - kalmanGain[1] * deltaFSBytes * t01; + t00 = theta_cov_[0][0]; + t01 = theta_cov_[0][1]; + theta_cov_[0][0] = (1 - kalmanGain[0] * delta_frame_size_bytes) * t00 - + kalmanGain[0] * theta_cov_[1][0]; + theta_cov_[0][1] = (1 - kalmanGain[0] * delta_frame_size_bytes) * t01 - + kalmanGain[0] * theta_cov_[1][1]; + theta_cov_[1][0] = theta_cov_[1][0] * (1 - kalmanGain[1]) - + kalmanGain[1] * delta_frame_size_bytes * t00; + theta_cov_[1][1] = theta_cov_[1][1] * (1 - kalmanGain[1]) - + kalmanGain[1] * delta_frame_size_bytes * t01; // Covariance matrix, must be positive semi-definite. - RTC_DCHECK(_thetaCov[0][0] + _thetaCov[1][1] >= 0 && - _thetaCov[0][0] * _thetaCov[1][1] - - _thetaCov[0][1] * _thetaCov[1][0] >= + RTC_DCHECK(theta_cov_[0][0] + theta_cov_[1][1] >= 0 && + theta_cov_[0][0] * theta_cov_[1][1] - + theta_cov_[0][1] * theta_cov_[1][0] >= 0 && - _thetaCov[0][0] >= 0); + theta_cov_[0][0] >= 0); } // Calculate difference in delay between a sample and the expected delay // estimated by the Kalman filter double VCMJitterEstimator::DeviationFromExpectedDelay( - int64_t frameDelayMS, - int32_t deltaFSBytes) const { - return frameDelayMS - (_theta[0] * deltaFSBytes + _theta[1]); + TimeDelta frame_delay, + double delta_frame_size_bytes) const { + return frame_delay.ms() - (theta_[0] * delta_frame_size_bytes + theta_[1]); } // Estimates the random jitter by calculating the variance of the sample // distance from the line given by theta. void VCMJitterEstimator::EstimateRandomJitter(double d_dT, - bool incompleteFrame) { - uint64_t now = clock_->TimeInMicroseconds(); - if (_lastUpdateT != -1) { - fps_counter_.AddSample(now - _lastUpdateT); + bool incomplete_frame) { + Timestamp now = clock_->CurrentTime(); + if (last_update_time_.has_value()) { + fps_counter_.AddSample((now - *last_update_time_).us()); } - _lastUpdateT = now; + last_update_time_ = now; - if (_alphaCount == 0) { + if (alpha_count_ == 0) { RTC_DCHECK_NOTREACHED(); return; } double alpha = - static_cast(_alphaCount - 1) / static_cast(_alphaCount); - _alphaCount++; - if (_alphaCount > _alphaCountMax) - _alphaCount = _alphaCountMax; + static_cast(alpha_count_ - 1) / static_cast(alpha_count_); + alpha_count_++; + if (alpha_count_ > kAlphaCountMax) + alpha_count_ = kAlphaCountMax; // In order to avoid a low frame rate stream to react slower to changes, // scale the alpha weight relative a 30 fps stream. - double fps = GetFrameRate(); - if (fps > 0.0) { - double rate_scale = 30.0 / fps; + Frequency fps = GetFrameRate(); + if (fps > Frequency::Zero()) { + constexpr Frequency k30Fps = Frequency::Hertz(30); + double rate_scale = k30Fps / fps; // At startup, there can be a lot of noise in the fps estimate. // Interpolate rate_scale linearly, from 1.0 at sample #1, to 30.0 / fps // at sample #kStartupDelaySamples. - if (_alphaCount < kStartupDelaySamples) { + if (alpha_count_ < kStartupDelaySamples) { rate_scale = - (_alphaCount * rate_scale + (kStartupDelaySamples - _alphaCount)) / + (alpha_count_ * rate_scale + (kStartupDelaySamples - alpha_count_)) / kStartupDelaySamples; } alpha = pow(alpha, rate_scale); } - double avgNoise = alpha * _avgNoise + (1 - alpha) * d_dT; - double varNoise = - alpha * _varNoise + (1 - alpha) * (d_dT - _avgNoise) * (d_dT - _avgNoise); - if (!incompleteFrame || varNoise > _varNoise) { - _avgNoise = avgNoise; - _varNoise = varNoise; + double avgNoise = alpha * avg_noise_ + (1 - alpha) * d_dT; + double varNoise = alpha * var_noise_ + + (1 - alpha) * (d_dT - avg_noise_) * (d_dT - avg_noise_); + if (!incomplete_frame || varNoise > var_noise_) { + avg_noise_ = avgNoise; + var_noise_ = varNoise; } - if (_varNoise < 1.0) { + if (var_noise_ < 1.0) { // The variance should never be zero, since we might get stuck and consider // all samples as outliers. - _varNoise = 1.0; + var_noise_ = 1.0; } } double VCMJitterEstimator::NoiseThreshold() const { - double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset; + double noiseThreshold = kNoiseStdDevs * sqrt(var_noise_) - kNoiseStdDevOffset; if (noiseThreshold < 1.0) { noiseThreshold = 1.0; } @@ -349,88 +331,91 @@ double VCMJitterEstimator::NoiseThreshold() const { } // Calculates the current jitter estimate from the filtered estimates. -double VCMJitterEstimator::CalculateEstimate() { - double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold(); +TimeDelta VCMJitterEstimator::CalculateEstimate() { + double retMs = + theta_[0] * (max_frame_size_.bytes() - avg_frame_size_.bytes()) + + NoiseThreshold(); + + TimeDelta ret = TimeDelta::Millis(retMs); + constexpr TimeDelta kMinPrevEstimate = TimeDelta::Micros(10); + constexpr TimeDelta kMaxEstimate = TimeDelta::Seconds(10); // A very low estimate (or negative) is neglected. - if (ret < 1.0) { - if (_prevEstimate <= 0.01) { - ret = 1.0; + if (ret < TimeDelta::Millis(1)) { + if (!prev_estimate_ || prev_estimate_ <= kMinPrevEstimate) { + ret = TimeDelta::Millis(1); } else { - ret = _prevEstimate; + ret = *prev_estimate_; } } - if (ret > 10000.0) { // Sanity - ret = 10000.0; + if (ret > kMaxEstimate) { // Sanity + ret = kMaxEstimate; } - _prevEstimate = ret; + prev_estimate_ = ret; return ret; } void VCMJitterEstimator::PostProcessEstimate() { - _filterJitterEstimate = CalculateEstimate(); + filter_jitter_estimate_ = CalculateEstimate(); } -void VCMJitterEstimator::UpdateRtt(int64_t rttMs) { - _rttFilter.Update(rttMs); +void VCMJitterEstimator::UpdateRtt(TimeDelta rtt) { + rtt_filter_.Update(rtt); } // Returns the current filtered estimate if available, // otherwise tries to calculate an estimate. -int VCMJitterEstimator::GetJitterEstimate( - double rttMultiplier, - absl::optional rttMultAddCapMs) { - double jitterMS = CalculateEstimate() + OPERATING_SYSTEM_JITTER; - uint64_t now = clock_->TimeInMicroseconds(); - - if (now - _latestNackTimestamp > kNackCountTimeoutMs * 1000) - _nackCount = 0; - - if (_filterJitterEstimate > jitterMS) - jitterMS = _filterJitterEstimate; - if (_nackCount >= _nackLimit) { - if (rttMultAddCapMs.has_value()) { - jitterMS += - std::min(_rttFilter.RttMs() * rttMultiplier, rttMultAddCapMs.value()); +TimeDelta VCMJitterEstimator::GetJitterEstimate( + double rtt_multiplier, + absl::optional rtt_mult_add_cap) { + TimeDelta jitter = CalculateEstimate() + OPERATING_SYSTEM_JITTER; + Timestamp now = clock_->CurrentTime(); + + if (now - latest_nack_ > kNackCountTimeout) + nack_count_ = 0; + + if (filter_jitter_estimate_ > jitter) + jitter = filter_jitter_estimate_; + if (nack_count_ >= kNackLimit) { + if (rtt_mult_add_cap.has_value()) { + jitter += std::min(rtt_filter_.Rtt() * rtt_multiplier, + rtt_mult_add_cap.value()); } else { - jitterMS += _rttFilter.RttMs() * rttMultiplier; + jitter += rtt_filter_.Rtt() * rtt_multiplier; } } if (enable_reduced_delay_) { - static const double kJitterScaleLowThreshold = 5.0; - static const double kJitterScaleHighThreshold = 10.0; - double fps = GetFrameRate(); + static const Frequency kJitterScaleLowThreshold = Frequency::Hertz(5); + static const Frequency kJitterScaleHighThreshold = Frequency::Hertz(10); + Frequency fps = GetFrameRate(); // Ignore jitter for very low fps streams. if (fps < kJitterScaleLowThreshold) { - if (fps == 0.0) { - return rtc::checked_cast(std::max(0.0, jitterMS) + 0.5); + if (fps.IsZero()) { + return std::max(TimeDelta::Zero(), jitter); } - return 0; + return TimeDelta::Zero(); } // Semi-low frame rate; scale by factor linearly interpolated from 0.0 at // kJitterScaleLowThreshold to 1.0 at kJitterScaleHighThreshold. if (fps < kJitterScaleHighThreshold) { - jitterMS = - (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) * - (fps - kJitterScaleLowThreshold) * jitterMS; + jitter = (1.0 / (kJitterScaleHighThreshold - kJitterScaleLowThreshold)) * + (fps - kJitterScaleLowThreshold) * jitter; } } - return rtc::checked_cast(std::max(0.0, jitterMS) + 0.5); + return std::max(TimeDelta::Zero(), jitter); } -double VCMJitterEstimator::GetFrameRate() const { - if (fps_counter_.ComputeMean() <= 0.0) - return 0; +Frequency VCMJitterEstimator::GetFrameRate() const { + TimeDelta mean_frame_period = TimeDelta::Micros(fps_counter_.ComputeMean()); + if (mean_frame_period <= TimeDelta::Zero()) + return Frequency::Zero(); - double fps = 1000000.0 / fps_counter_.ComputeMean(); + Frequency fps = 1 / mean_frame_period; // Sanity check. - RTC_DCHECK_GE(fps, 0.0); - if (fps > kMaxFramerateEstimate) { - fps = kMaxFramerateEstimate; - } - return fps; + RTC_DCHECK_GE(fps, Frequency::Zero()); + return std::min(fps, kMaxFramerateEstimate); } } // namespace webrtc diff --git a/modules/video_coding/jitter_estimator.h b/modules/video_coding/jitter_estimator.h index 1d69b95769..20d318a534 100644 --- a/modules/video_coding/jitter_estimator.h +++ b/modules/video_coding/jitter_estimator.h @@ -11,6 +11,12 @@ #ifndef MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ #define MODULES_VIDEO_CODING_JITTER_ESTIMATOR_H_ +#include "absl/types/optional.h" +#include "api/field_trials_view.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" #include "modules/video_coding/rtt_filter.h" #include "rtc_base/rolling_accumulator.h" @@ -20,9 +26,11 @@ class Clock; class VCMJitterEstimator { public: - explicit VCMJitterEstimator(Clock* clock); + explicit VCMJitterEstimator(Clock* clock, + const FieldTrialsView& field_trials); virtual ~VCMJitterEstimator(); - VCMJitterEstimator& operator=(const VCMJitterEstimator& rhs); + VCMJitterEstimator(const VCMJitterEstimator&) = delete; + VCMJitterEstimator& operator=(const VCMJitterEstimator&) = delete; // Resets the estimate to the initial state. void Reset(); @@ -30,24 +38,25 @@ class VCMJitterEstimator { // Updates the jitter estimate with the new data. // // Input: - // - frameDelay : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - frameSize : Frame size of the current frame. - // - incompleteFrame : Flags if the frame is used to update the + // - frame_delay : Delay-delta calculated by UTILDelayEstimate. + // - frame_size : Frame size of the current frame. + // - incomplete_frame : Flags if the frame is used to update the // estimate before it was complete. // Default is false. - void UpdateEstimate(int64_t frameDelayMS, - uint32_t frameSizeBytes, - bool incompleteFrame = false); + void UpdateEstimate(TimeDelta frame_delay, + DataSize frame_size, + bool incomplete_frame = false); - // Returns the current jitter estimate in milliseconds and adds an RTT - // dependent term in cases of retransmission. + // Returns the current jitter estimate and adds an RTT dependent term in cases + // of retransmission. // Input: - // - rttMultiplier : RTT param multiplier (when applicable). + // - rtt_multiplier : RTT param multiplier (when applicable). + // - rtt_mult_add_cap : Multiplier cap from the RTTMultExperiment. // - // Return value : Jitter estimate in milliseconds. - virtual int GetJitterEstimate(double rttMultiplier, - absl::optional rttMultAddCapMs); + // Return value : Jitter estimate. + virtual TimeDelta GetJitterEstimate( + double rtt_multiplier, + absl::optional rtt_mult_add_cap); // Updates the nack counter. void FrameNacked(); @@ -55,45 +64,47 @@ class VCMJitterEstimator { // Updates the RTT filter. // // Input: - // - rttMs : RTT in ms. - void UpdateRtt(int64_t rttMs); + // - rtt : Round trip time. + void UpdateRtt(TimeDelta rtt); // A constant describing the delay from the jitter buffer to the delay on the // receiving side which is not accounted for by the jitter buffer nor the // decoding delay estimate. - static const uint32_t OPERATING_SYSTEM_JITTER = 10; + static constexpr TimeDelta OPERATING_SYSTEM_JITTER = TimeDelta::Millis(10); protected: // These are protected for better testing possibilities. - double _theta[2]; // Estimated line parameters (slope, offset) - double _varNoise; // Variance of the time-deviation from the line + double theta_[2]; // Estimated line parameters (slope, offset) + double var_noise_; // Variance of the time-deviation from the line private: // Updates the Kalman filter for the line describing the frame size dependent // jitter. // // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - deltaFSBytes : Frame size delta, i.e. frame size at time T - // : minus frame size at time T-1. - void KalmanEstimateChannel(int64_t frameDelayMS, int32_t deltaFSBytes); + // - frame_delay + // Delay-delta calculated by UTILDelayEstimate. + // - delta_frame_size_bytes + // Frame size delta, i.e. frame size at time T minus frame size + // at time T-1. + void KalmanEstimateChannel(TimeDelta frame_delay, + double delta_frame_size_bytes); // Updates the random jitter estimate, i.e. the variance of the time // deviations from the line given by the Kalman filter. // // Input: // - d_dT : The deviation from the kalman estimate. - // - incompleteFrame : True if the frame used to update the + // - incomplete_frame : True if the frame used to update the // estimate with was incomplete. - void EstimateRandomJitter(double d_dT, bool incompleteFrame); + void EstimateRandomJitter(double d_dT, bool incomplete_frame); double NoiseThreshold() const; // Calculates the current jitter estimate. // - // Return value : The current jitter estimate in milliseconds. - double CalculateEstimate(); + // Return value : The current jitter estimate. + TimeDelta CalculateEstimate(); // Post process the calculated estimate. void PostProcessEstimate(); @@ -102,52 +113,48 @@ class VCMJitterEstimator { // estimated by the Kalman filter. // // Input: - // - frameDelayMS : Delay-delta calculated by UTILDelayEstimate in - // milliseconds. - // - deltaFS : Frame size delta, i.e. frame size at time - // T minus frame size at time T-1. + // - frame_delay : Delay-delta calculated by UTILDelayEstimate. + // - delta_frame_size_bytes : Frame size delta, i.e. frame size at + // time + // T minus frame size at time T-1. // - // Return value : The difference in milliseconds. - double DeviationFromExpectedDelay(int64_t frameDelayMS, - int32_t deltaFSBytes) const; - - double GetFrameRate() const; - - // Constants, filter parameters. - const double _phi; - const double _psi; - const uint32_t _alphaCountMax; - const double _thetaLow; - const uint32_t _nackLimit; - const int32_t _numStdDevDelayOutlier; - const int32_t _numStdDevFrameSizeOutlier; - const double _noiseStdDevs; - const double _noiseStdDevOffset; - - double _thetaCov[2][2]; // Estimate covariance - double _Qcov[2][2]; // Process noise covariance - double _avgFrameSize; // Average frame size - double _varFrameSize; // Frame size variance - double _maxFrameSize; // Largest frame size received (descending - // with a factor _psi) - uint32_t _fsSum; - uint32_t _fsCount; - - int64_t _lastUpdateT; - double _prevEstimate; // The previously returned jitter estimate - uint32_t _prevFrameSize; // Frame size of the previous frame - double _avgNoise; // Average of the random jitter - uint32_t _alphaCount; - double _filterJitterEstimate; // The filtered sum of jitter estimates - - uint32_t _startupCount; - - int64_t - _latestNackTimestamp; // Timestamp in ms when the latest nack was seen - uint32_t _nackCount; // Keeps track of the number of nacks received, - // but never goes above _nackLimit - VCMRttFilter _rttFilter; - + // Return value : The delay difference in ms. + double DeviationFromExpectedDelay(TimeDelta frame_delay, + double delta_frame_size_bytes) const; + + Frequency GetFrameRate() const; + + double theta_cov_[2][2]; // Estimate covariance + double q_cov_[2][2]; // Process noise covariance + + static constexpr DataSize kDefaultAvgAndMaxFrameSize = DataSize::Bytes(500); + DataSize avg_frame_size_ = kDefaultAvgAndMaxFrameSize; // Average frame size + double var_frame_size_; // Frame size variance. Unit is bytes^2. + // Largest frame size received (descending with a factor kPsi) + DataSize max_frame_size_ = kDefaultAvgAndMaxFrameSize; + DataSize frame_size_sum_ = DataSize::Zero(); + uint32_t frame_size_count_; + + absl::optional last_update_time_; + // The previously returned jitter estimate + absl::optional prev_estimate_; + // Frame size of the previous frame + absl::optional prev_frame_size_; + // Average of the random jitter + double avg_noise_; + uint32_t alpha_count_; + // The filtered sum of jitter estimates + TimeDelta filter_jitter_estimate_ = TimeDelta::Zero(); + + uint32_t startup_count_; + // Time when the latest nack was seen + Timestamp latest_nack_ = Timestamp::Zero(); + // Keeps track of the number of nacks received, but never goes above + // kNackLimit. + uint32_t nack_count_; + VCMRttFilter rtt_filter_; + + // Tracks frame rates in microseconds. rtc::RollingAccumulator fps_counter_; const double time_deviation_upper_bound_; const bool enable_reduced_delay_; diff --git a/modules/video_coding/jitter_estimator_tests.cc b/modules/video_coding/jitter_estimator_tests.cc index 14baae7e81..b1df95bc74 100644 --- a/modules/video_coding/jitter_estimator_tests.cc +++ b/modules/video_coding/jitter_estimator_tests.cc @@ -14,14 +14,17 @@ #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/units/data_size.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" #include "modules/video_coding/jitter_estimator.h" #include "rtc_base/experiments/jitter_upper_bound_experiment.h" #include "rtc_base/numerics/histogram_percentile_counter.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/time_utils.h" #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -30,14 +33,12 @@ class TestVCMJitterEstimator : public ::testing::Test { TestVCMJitterEstimator() : fake_clock_(0) {} virtual void SetUp() { - estimator_ = std::make_unique(&fake_clock_); - } - - void AdvanceClock(int64_t microseconds) { - fake_clock_.AdvanceTimeMicroseconds(microseconds); + estimator_ = + std::make_unique(&fake_clock_, field_trials_); } SimulatedClock fake_clock_; + test::ScopedKeyValueConfig field_trials_; std::unique_ptr estimator_; }; @@ -46,11 +47,16 @@ class ValueGenerator { public: explicit ValueGenerator(int32_t amplitude) : amplitude_(amplitude), counter_(0) {} - virtual ~ValueGenerator() {} - int64_t Delay() const { return ((counter_ % 11) - 5) * amplitude_; } + virtual ~ValueGenerator() = default; - uint32_t FrameSize() const { return 1000 + Delay(); } + TimeDelta Delay() const { + return TimeDelta::Millis((counter_ % 11) - 5) * amplitude_; + } + + DataSize FrameSize() const { + return DataSize::Bytes(1000 + Delay().ms() / 5); + } void Advance() { ++counter_; } @@ -62,28 +68,30 @@ class ValueGenerator { // 5 fps, disable jitter delay altogether. TEST_F(TestVCMJitterEstimator, TestLowRate) { ValueGenerator gen(10); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; + TimeDelta time_delta = 1 / Frequency::Hertz(5); for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); if (i > 2) - EXPECT_EQ(estimator_->GetJitterEstimate(0, absl::nullopt), 0); + EXPECT_EQ(estimator_->GetJitterEstimate(0, absl::nullopt), + TimeDelta::Zero()); gen.Advance(); } } TEST_F(TestVCMJitterEstimator, TestLowRateDisabled) { - test::ScopedFieldTrials field_trials( - "WebRTC-ReducedJitterDelayKillSwitch/Enabled/"); + test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-ReducedJitterDelayKillSwitch/Enabled/"); SetUp(); ValueGenerator gen(10); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 5; + TimeDelta time_delta = 1 / Frequency::Hertz(5); for (int i = 0; i < 60; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); if (i > 2) - EXPECT_GT(estimator_->GetJitterEstimate(0, absl::nullopt), 0); + EXPECT_GT(estimator_->GetJitterEstimate(0, absl::nullopt), + TimeDelta::Zero()); gen.Advance(); } } @@ -97,7 +105,7 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { percentiles(1000) {} double upper_bound; double rtt_mult; - absl::optional rtt_mult_add_cap_ms; + absl::optional rtt_mult_add_cap_ms; rtc::HistogramPercentileCounter percentiles; }; std::vector test_cases(4); @@ -113,11 +121,11 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { // Large upper bound, rtt_mult = 1, and large rtt_mult addition cap value. test_cases[2].upper_bound = 1000.0; test_cases[2].rtt_mult = 1.0; - test_cases[2].rtt_mult_add_cap_ms = 200.0; + test_cases[2].rtt_mult_add_cap_ms = TimeDelta::Millis(200); // Large upper bound, rtt_mult = 1, and small rtt_mult addition cap value. test_cases[3].upper_bound = 1000.0; test_cases[3].rtt_mult = 1.0; - test_cases[3].rtt_mult_add_cap_ms = 10.0; + test_cases[3].rtt_mult_add_cap_ms = TimeDelta::Millis(10); // Test jitter buffer upper_bound and rtt_mult addition cap sizes. for (TestContext& context : test_cases) { @@ -126,20 +134,21 @@ TEST_F(TestVCMJitterEstimator, TestUpperBound) { rtc::SimpleStringBuilder ssb(string_buf); ssb << JitterUpperBoundExperiment::kJitterUpperBoundExperimentName << "/Enabled-" << context.upper_bound << "/"; - test::ScopedFieldTrials field_trials(ssb.str()); + test::ScopedKeyValueConfig field_trials(field_trials_, ssb.str()); SetUp(); ValueGenerator gen(50); - uint64_t time_delta_us = rtc::kNumMicrosecsPerSec / 30; - constexpr int64_t kRttMs = 250; + TimeDelta time_delta = 1 / Frequency::Hertz(30); + constexpr TimeDelta kRtt = TimeDelta::Millis(250); for (int i = 0; i < 100; ++i) { estimator_->UpdateEstimate(gen.Delay(), gen.FrameSize()); - AdvanceClock(time_delta_us); + fake_clock_.AdvanceTime(time_delta); estimator_->FrameNacked(); // To test rtt_mult. - estimator_->UpdateRtt(kRttMs); // To test rtt_mult. + estimator_->UpdateRtt(kRtt); // To test rtt_mult. context.percentiles.Add( - static_cast(estimator_->GetJitterEstimate( - context.rtt_mult, context.rtt_mult_add_cap_ms))); + estimator_ + ->GetJitterEstimate(context.rtt_mult, context.rtt_mult_add_cap_ms) + .ms()); gen.Advance(); } } diff --git a/modules/video_coding/media_opt_util.h b/modules/video_coding/media_opt_util.h index 1d98ea6ca4..a74d1af6cb 100644 --- a/modules/video_coding/media_opt_util.h +++ b/modules/video_coding/media_opt_util.h @@ -26,10 +26,10 @@ namespace media_optimization { // Number of time periods used for (max) window filter for packet loss // TODO(marpan): set reasonable window size for filtered packet loss, // adjustment should be based on logged/real data of loss stats/correlation. -enum { kLossPrHistorySize = 10 }; +constexpr int kLossPrHistorySize = 10; // 1000 ms, total filter length is (kLossPrHistorySize * 1000) ms -enum { kLossPrShortFilterWinMs = 1000 }; +constexpr int kLossPrShortFilterWinMs = 1000; // The type of filter used on the received packet loss reports. enum FilterPacketLossMode { @@ -41,11 +41,11 @@ enum FilterPacketLossMode { // Thresholds for hybrid NACK/FEC // common to media optimization and the jitter buffer. -const int64_t kLowRttNackMs = 20; +constexpr int64_t kLowRttNackMs = 20; // If the RTT is higher than this an extra RTT wont be added to to the jitter // buffer delay. -const int kMaxRttDelayThreshold = 500; +constexpr int kMaxRttDelayThreshold = 500; struct VCMProtectionParameters { VCMProtectionParameters(); @@ -175,15 +175,15 @@ class VCMFecMethod : public VCMProtectionMethod { int BitsPerFrame(const VCMProtectionParameters* parameters); protected: - enum { kUpperLimitFramesFec = 6 }; + static constexpr int kUpperLimitFramesFec = 6; // Thresholds values for the bytes/frame and round trip time, below which we // may turn off FEC, depending on `_numLayers` and `_maxFramesFec`. // Max bytes/frame for VGA, corresponds to ~140k at 25fps. - enum { kMaxBytesPerFrameForFec = 700 }; + static constexpr int kMaxBytesPerFrameForFec = 700; // Max bytes/frame for CIF and lower: corresponds to ~80k at 25fps. - enum { kMaxBytesPerFrameForFecLow = 400 }; + static constexpr int kMaxBytesPerFrameForFecLow = 400; // Max bytes/frame for frame size larger than VGA, ~200k at 25fps. - enum { kMaxBytesPerFrameForFecHigh = 1000 }; + static constexpr int kMaxBytesPerFrameForFecHigh = 1000; const RateControlSettings rate_control_settings_; }; diff --git a/modules/video_coding/nack_module_unittest.cc b/modules/video_coding/nack_module_unittest.cc index f91eb750f0..704f2cdae9 100644 --- a/modules/video_coding/nack_module_unittest.cc +++ b/modules/video_coding/nack_module_unittest.cc @@ -16,8 +16,8 @@ #include #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { class TestNackModule : public ::testing::TestWithParam, @@ -29,7 +29,7 @@ class TestNackModule : public ::testing::TestWithParam, field_trial_(GetParam() ? "WebRTC-ExponentialNackBackoff/enabled:true/" : "WebRTC-ExponentialNackBackoff/enabled:false/"), - nack_module_(clock_.get(), this, this), + nack_module_(clock_.get(), this, this, field_trial_), keyframes_requested_(0) {} void SetUp() override { nack_module_.UpdateRtt(kDefaultRttMs); } @@ -44,7 +44,7 @@ class TestNackModule : public ::testing::TestWithParam, static constexpr int64_t kDefaultRttMs = 20; std::unique_ptr clock_; - test::ScopedFieldTrials field_trial_; + test::ScopedKeyValueConfig field_trial_; DEPRECATED_NackModule nack_module_; std::vector sent_nacks_; int keyframes_requested_; @@ -339,7 +339,7 @@ class TestNackModuleWithFieldTrial : public ::testing::Test, TestNackModuleWithFieldTrial() : nack_delay_field_trial_("WebRTC-SendNackDelayMs/10/"), clock_(new SimulatedClock(0)), - nack_module_(clock_.get(), this, this), + nack_module_(clock_.get(), this, this, nack_delay_field_trial_), keyframes_requested_(0) {} void SendNack(const std::vector& sequence_numbers, @@ -350,7 +350,7 @@ class TestNackModuleWithFieldTrial : public ::testing::Test, void RequestKeyFrame() override { ++keyframes_requested_; } - test::ScopedFieldTrials nack_delay_field_trial_; + test::ScopedKeyValueConfig nack_delay_field_trial_; std::unique_ptr clock_; DEPRECATED_NackModule nack_module_; std::vector sent_nacks_; diff --git a/modules/video_coding/nack_requester.cc b/modules/video_coding/nack_requester.cc index dac85fc189..1db716b3ea 100644 --- a/modules/video_coding/nack_requester.cc +++ b/modules/video_coding/nack_requester.cc @@ -20,7 +20,6 @@ #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" #include "rtc_base/task_queue.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { @@ -33,10 +32,9 @@ const int kMaxReorderedPackets = 128; const int kNumReorderingBuckets = 10; const int kDefaultSendNackDelayMs = 0; -int64_t GetSendNackDelay() { +int64_t GetSendNackDelay(const FieldTrialsView& field_trials) { int64_t delay_ms = strtol( - webrtc::field_trial::FindFullName("WebRTC-SendNackDelayMs").c_str(), - nullptr, 10); + field_trials.Lookup("WebRTC-SendNackDelayMs").c_str(), nullptr, 10); if (delay_ms > 0 && delay_ms <= 20) { RTC_LOG(LS_INFO) << "SendNackDelay is set to " << delay_ms; return delay_ms; @@ -110,7 +108,8 @@ NackRequester::BackoffSettings::BackoffSettings(TimeDelta min_retry, : min_retry_interval(min_retry), max_rtt(max_rtt), base(base) {} absl::optional -NackRequester::BackoffSettings::ParseFromFieldTrials() { +NackRequester::BackoffSettings::ParseFromFieldTrials( + const FieldTrialsView& field_trials) { // Matches magic number in RTPSender::OnReceivedNack(). const TimeDelta kDefaultMinRetryInterval = TimeDelta::Millis(5); // Upper bound on link-delay considered for exponential backoff. @@ -126,7 +125,7 @@ NackRequester::BackoffSettings::ParseFromFieldTrials() { FieldTrialParameter max_rtt("max_rtt", kDefaultMaxRtt); FieldTrialParameter base("base", kDefaultBase); ParseFieldTrial({&enabled, &min_retry, &max_rtt, &base}, - field_trial::FindFullName("WebRTC-ExponentialNackBackoff")); + field_trials.Lookup("WebRTC-ExponentialNackBackoff")); if (enabled) { return NackRequester::BackoffSettings(min_retry.Get(), max_rtt.Get(), @@ -139,7 +138,8 @@ NackRequester::NackRequester(TaskQueueBase* current_queue, NackPeriodicProcessor* periodic_processor, Clock* clock, NackSender* nack_sender, - KeyFrameRequestSender* keyframe_request_sender) + KeyFrameRequestSender* keyframe_request_sender, + const FieldTrialsView& field_trials) : worker_thread_(current_queue), clock_(clock), nack_sender_(nack_sender), @@ -148,8 +148,8 @@ NackRequester::NackRequester(TaskQueueBase* current_queue, initialized_(false), rtt_ms_(kDefaultRttMs), newest_seq_num_(0), - send_nack_delay_ms_(GetSendNackDelay()), - backoff_settings_(BackoffSettings::ParseFromFieldTrials()), + send_nack_delay_ms_(GetSendNackDelay(field_trials)), + backoff_settings_(BackoffSettings::ParseFromFieldTrials(field_trials)), processor_registration_(this, periodic_processor) { RTC_DCHECK(clock_); RTC_DCHECK(nack_sender_); diff --git a/modules/video_coding/nack_requester.h b/modules/video_coding/nack_requester.h index 46d904b7a4..fc44a59419 100644 --- a/modules/video_coding/nack_requester.h +++ b/modules/video_coding/nack_requester.h @@ -17,6 +17,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "api/units/time_delta.h" #include "modules/include/module_common_types.h" @@ -70,7 +71,8 @@ class NackRequester final : public NackRequesterBase { NackPeriodicProcessor* periodic_processor, Clock* clock, NackSender* nack_sender, - KeyFrameRequestSender* keyframe_request_sender); + KeyFrameRequestSender* keyframe_request_sender, + const FieldTrialsView& field_trials); ~NackRequester(); void ProcessNacks() override; @@ -104,7 +106,8 @@ class NackRequester final : public NackRequesterBase { struct BackoffSettings { BackoffSettings(TimeDelta min_retry, TimeDelta max_rtt, double base); - static absl::optional ParseFromFieldTrials(); + static absl::optional ParseFromFieldTrials( + const FieldTrialsView& field_trials); // Min time between nacks. const TimeDelta min_retry_interval; diff --git a/modules/video_coding/nack_requester_unittest.cc b/modules/video_coding/nack_requester_unittest.cc index 0e5d4159d3..22342491db 100644 --- a/modules/video_coding/nack_requester_unittest.cc +++ b/modules/video_coding/nack_requester_unittest.cc @@ -16,9 +16,9 @@ #include #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" #include "test/run_loop.h" +#include "test/scoped_key_value_config.h" namespace webrtc { // TODO(bugs.webrtc.org/11594): Use the use the GlobalSimulatedTimeController @@ -86,7 +86,7 @@ class TestNackRequester : public ::testing::TestWithParam, std::make_unique(interval); nack_module_ = std::make_unique( TaskQueueBase::Current(), nack_periodic_processor_.get(), clock_.get(), - this, this); + this, this, field_trial_); nack_module_->UpdateRtt(kDefaultRttMs); return *nack_module_.get(); } @@ -94,7 +94,7 @@ class TestNackRequester : public ::testing::TestWithParam, static constexpr int64_t kDefaultRttMs = 20; test::RunLoop loop_; std::unique_ptr clock_; - test::ScopedFieldTrials field_trial_; + test::ScopedKeyValueConfig field_trial_; std::unique_ptr nack_periodic_processor_; std::unique_ptr nack_module_; std::vector sent_nacks_; @@ -387,7 +387,8 @@ class TestNackRequesterWithFieldTrial : public ::testing::Test, &nack_periodic_processor_, clock_.get(), this, - this), + this, + nack_delay_field_trial_), keyframes_requested_(0) {} void SendNack(const std::vector& sequence_numbers, @@ -398,7 +399,7 @@ class TestNackRequesterWithFieldTrial : public ::testing::Test, void RequestKeyFrame() override { ++keyframes_requested_; } - test::ScopedFieldTrials nack_delay_field_trial_; + test::ScopedKeyValueConfig nack_delay_field_trial_; std::unique_ptr clock_; NackPeriodicProcessor nack_periodic_processor_; NackRequester nack_module_; diff --git a/modules/video_coding/packet_buffer.cc b/modules/video_coding/packet_buffer.cc index 9ad56cae4a..adc18ddce0 100644 --- a/modules/video_coding/packet_buffer.cc +++ b/modules/video_coding/packet_buffer.cc @@ -242,11 +242,17 @@ std::vector> PacketBuffer::FindFrames( bool is_h264_keyframe = false; int idr_width = -1; int idr_height = -1; + bool full_frame_found = false; while (true) { ++tested_packets; - if (!is_h264 && buffer_[start_index]->is_first_packet_in_frame()) - break; + if (!is_h264) { + if (buffer_[start_index] == nullptr || + buffer_[start_index]->is_first_packet_in_frame()) { + full_frame_found = buffer_[start_index] != nullptr; + break; + } + } if (is_h264) { const auto* h264_header = absl::get_if( @@ -336,22 +342,24 @@ std::vector> PacketBuffer::FindFrames( } } - const uint16_t end_seq_num = seq_num + 1; - // Use uint16_t type to handle sequence number wrap around case. - uint16_t num_packets = end_seq_num - start_seq_num; - found_frames.reserve(found_frames.size() + num_packets); - for (uint16_t i = start_seq_num; i != end_seq_num; ++i) { - std::unique_ptr& packet = buffer_[i % buffer_.size()]; - RTC_DCHECK(packet); - RTC_DCHECK_EQ(i, packet->seq_num); - // Ensure frame boundary flags are properly set. - packet->video_header.is_first_packet_in_frame = (i == start_seq_num); - packet->video_header.is_last_packet_in_frame = (i == seq_num); - found_frames.push_back(std::move(packet)); - } + if (is_h264 || full_frame_found) { + const uint16_t end_seq_num = seq_num + 1; + // Use uint16_t type to handle sequence number wrap around case. + uint16_t num_packets = end_seq_num - start_seq_num; + found_frames.reserve(found_frames.size() + num_packets); + for (uint16_t i = start_seq_num; i != end_seq_num; ++i) { + std::unique_ptr& packet = buffer_[i % buffer_.size()]; + RTC_DCHECK(packet); + RTC_DCHECK_EQ(i, packet->seq_num); + // Ensure frame boundary flags are properly set. + packet->video_header.is_first_packet_in_frame = (i == start_seq_num); + packet->video_header.is_last_packet_in_frame = (i == seq_num); + found_frames.push_back(std::move(packet)); + } - missing_packets_.erase(missing_packets_.begin(), - missing_packets_.upper_bound(seq_num)); + missing_packets_.erase(missing_packets_.begin(), + missing_packets_.upper_bound(seq_num)); + } } ++seq_num; } diff --git a/modules/video_coding/packet_buffer_unittest.cc b/modules/video_coding/packet_buffer_unittest.cc index cc1f1779ea..2ad771443e 100644 --- a/modules/video_coding/packet_buffer_unittest.cc +++ b/modules/video_coding/packet_buffer_unittest.cc @@ -291,6 +291,13 @@ TEST_F(PacketBufferTest, ClearSinglePacket) { Insert(seq_num + kMaxSize, kDeltaFrame, kFirst, kLast).buffer_cleared); } +TEST_F(PacketBufferTest, ClearPacketBeforeFullyReceivedFrame) { + Insert(0, kKeyFrame, kFirst, kNotLast); + Insert(1, kKeyFrame, kNotFirst, kNotLast); + packet_buffer_.ClearTo(0); + EXPECT_THAT(Insert(2, kKeyFrame, kNotFirst, kLast).packets, IsEmpty()); +} + TEST_F(PacketBufferTest, ClearFullBuffer) { for (int i = 0; i < kMaxSize; ++i) Insert(i, kDeltaFrame, kFirst, kLast); diff --git a/modules/video_coding/receiver.cc b/modules/video_coding/receiver.cc index e156a1c28d..3f954ec9bf 100644 --- a/modules/video_coding/receiver.cc +++ b/modules/video_coding/receiver.cc @@ -30,18 +30,22 @@ namespace webrtc { enum { kMaxReceiverDelayMs = 10000 }; -VCMReceiver::VCMReceiver(VCMTiming* timing, Clock* clock) +VCMReceiver::VCMReceiver(VCMTiming* timing, + Clock* clock, + const FieldTrialsView& field_trials) : VCMReceiver::VCMReceiver(timing, clock, absl::WrapUnique(EventWrapper::Create()), - absl::WrapUnique(EventWrapper::Create())) {} + absl::WrapUnique(EventWrapper::Create()), + field_trials) {} VCMReceiver::VCMReceiver(VCMTiming* timing, Clock* clock, std::unique_ptr receiver_event, - std::unique_ptr jitter_buffer_event) + std::unique_ptr jitter_buffer_event, + const FieldTrialsView& field_trials) : clock_(clock), - jitter_buffer_(clock_, std::move(jitter_buffer_event)), + jitter_buffer_(clock_, std::move(jitter_buffer_event), field_trials), timing_(timing), render_wait_event_(std::move(receiver_event)), max_video_delay_ms_(kMaxVideoDelayMs) { @@ -69,7 +73,7 @@ int32_t VCMReceiver::InsertPacket(const VCMPacket& packet) { // We don't want to include timestamps which have suffered from // retransmission here, since we compensate with extra retransmission // delay within the jitter estimate. - timing_->IncomingTimestamp(packet.timestamp, clock_->TimeInMilliseconds()); + timing_->IncomingTimestamp(packet.timestamp, clock_->CurrentTime()); } return VCM_OK; } @@ -94,16 +98,18 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, } if (min_playout_delay_ms >= 0) - timing_->set_min_playout_delay(min_playout_delay_ms); + timing_->set_min_playout_delay(TimeDelta::Millis(min_playout_delay_ms)); if (max_playout_delay_ms >= 0) - timing_->set_max_playout_delay(max_playout_delay_ms); + timing_->set_max_playout_delay(TimeDelta::Millis(max_playout_delay_ms)); // We have a frame - Set timing and render timestamp. - timing_->SetJitterDelay(jitter_buffer_.EstimatedJitterMs()); - const int64_t now_ms = clock_->TimeInMilliseconds(); + timing_->SetJitterDelay( + TimeDelta::Millis(jitter_buffer_.EstimatedJitterMs())); + const Timestamp now = clock_->CurrentTime(); + const int64_t now_ms = now.ms(); timing_->UpdateCurrentDelay(frame_timestamp); - render_time_ms = timing_->RenderTimeMs(frame_timestamp, now_ms); + render_time_ms = timing_->RenderTime(frame_timestamp, now).ms(); // Check render timing. bool timing_error = false; // Assume that render timing errors are due to changes in the video stream. @@ -117,7 +123,7 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, << frame_delay << " > " << max_video_delay_ms_ << "). Resetting the video jitter buffer."; timing_error = true; - } else if (static_cast(timing_->TargetVideoDelay()) > + } else if (static_cast(timing_->TargetVideoDelay().ms()) > max_video_delay_ms_) { RTC_LOG(LS_WARNING) << "The video target delay has grown larger than " << max_video_delay_ms_ @@ -140,8 +146,11 @@ VCMEncodedFrame* VCMReceiver::FrameForDecoding(uint16_t max_wait_time_ms, uint16_t new_max_wait_time = static_cast(VCM_MAX(available_wait_time, 0)); uint32_t wait_time_ms = rtc::saturated_cast( - timing_->MaxWaitingTime(render_time_ms, clock_->TimeInMilliseconds(), - /*too_many_frames_queued=*/false)); + timing_ + ->MaxWaitingTime(Timestamp::Millis(render_time_ms), + clock_->CurrentTime(), + /*too_many_frames_queued=*/false) + .ms()); if (new_max_wait_time < wait_time_ms) { // We're not allowed to wait until the frame is supposed to be rendered, // waiting as long as we're allowed to avoid busy looping, and then return diff --git a/modules/video_coding/receiver.h b/modules/video_coding/receiver.h index 8f6b041a5a..0bf756cdd8 100644 --- a/modules/video_coding/receiver.h +++ b/modules/video_coding/receiver.h @@ -14,6 +14,7 @@ #include #include +#include "api/field_trials_view.h" #include "modules/video_coding/event_wrapper.h" #include "modules/video_coding/include/video_coding.h" #include "modules/video_coding/include/video_coding_defines.h" @@ -28,7 +29,9 @@ class VCMEncodedFrame; class VCMReceiver { public: - VCMReceiver(VCMTiming* timing, Clock* clock); + VCMReceiver(VCMTiming* timing, + Clock* clock, + const FieldTrialsView& field_trials); // Using this constructor, you can specify a different event implemetation for // the jitter buffer. Useful for unit tests when you want to simulate incoming @@ -37,7 +40,8 @@ class VCMReceiver { VCMReceiver(VCMTiming* timing, Clock* clock, std::unique_ptr receiver_event, - std::unique_ptr jitter_buffer_event); + std::unique_ptr jitter_buffer_event, + const FieldTrialsView& field_trials); ~VCMReceiver(); diff --git a/modules/video_coding/receiver_unittest.cc b/modules/video_coding/receiver_unittest.cc index 1b6ee34bd7..a9755b748d 100644 --- a/modules/video_coding/receiver_unittest.cc +++ b/modules/video_coding/receiver_unittest.cc @@ -24,6 +24,7 @@ #include "rtc_base/checks.h" #include "system_wrappers/include/clock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -31,8 +32,8 @@ class TestVCMReceiver : public ::testing::Test { protected: TestVCMReceiver() : clock_(0), - timing_(&clock_), - receiver_(&timing_, &clock_), + timing_(&clock_, field_trials_), + receiver_(&timing_, &clock_, field_trials_), stream_generator_(0, clock_.TimeInMilliseconds()) {} int32_t InsertPacket(int index) { @@ -78,6 +79,7 @@ class TestVCMReceiver : public ::testing::Test { return true; } + test::ScopedKeyValueConfig field_trials_; SimulatedClock clock_; VCMTiming timing_; VCMReceiver receiver_; @@ -124,7 +126,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -152,7 +154,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -182,7 +184,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert enough frames to have too long non-decodable sequence, except that @@ -212,7 +214,7 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) { const int kMinDelayMs = 500; receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, kMaxNonDecodableDuration); - timing_.set_min_playout_delay(kMinDelayMs); + timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); int64_t key_frame_inserted = clock_.TimeInMilliseconds(); EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); // Insert an incomplete frame. @@ -365,16 +367,17 @@ class VCMReceiverTimingTest : public ::testing::Test { VCMReceiverTimingTest() : clock_(&stream_generator_, &receiver_), stream_generator_(0, clock_.TimeInMilliseconds()), - timing_(&clock_), + timing_(&clock_, field_trials_), receiver_( &timing_, &clock_, std::unique_ptr(new FrameInjectEvent(&clock_, false)), - std::unique_ptr( - new FrameInjectEvent(&clock_, true))) {} + std::unique_ptr(new FrameInjectEvent(&clock_, true)), + field_trials_) {} virtual void SetUp() {} + test::ScopedKeyValueConfig field_trials_; SimulatedClockWithFrames clock_; StreamGenerator stream_generator_; VCMTiming timing_; @@ -448,11 +451,9 @@ TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { int64_t arrive_timestamps[kNumFrames]; int64_t render_timestamps[kNumFrames]; - int render_delay_ms; - int max_decode_ms; - int dummy; - timing_.GetTimings(&max_decode_ms, &dummy, &dummy, &dummy, &dummy, - &render_delay_ms); + auto timings = timing_.GetTimings(); + TimeDelta render_delay = timings.render_delay; + TimeDelta max_decode = timings.max_decode_duration; // Construct test samples. // render_timestamps are the timestamps stored in the Frame; @@ -479,7 +480,7 @@ TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding); int64_t end_time = clock_.TimeInMilliseconds(); if (frame) { - EXPECT_EQ(frame->RenderTimeMs() - max_decode_ms - render_delay_ms, + EXPECT_EQ(frame->RenderTimeMs() - max_decode.ms() - render_delay.ms(), end_time); receiver_.ReleaseFrame(frame); ++num_frames_return; diff --git a/modules/video_coding/rtp_vp8_ref_finder.cc b/modules/video_coding/rtp_vp8_ref_finder.cc index 64f1bae300..85fe18a263 100644 --- a/modules/video_coding/rtp_vp8_ref_finder.cc +++ b/modules/video_coding/rtp_vp8_ref_finder.cc @@ -18,14 +18,20 @@ namespace webrtc { RtpFrameReferenceFinder::ReturnVector RtpVp8RefFinder::ManageFrame( std::unique_ptr frame) { - FrameDecision decision = ManageFrameInternal(frame.get()); + const RTPVideoHeaderVP8& codec_header = absl::get( + frame->GetRtpVideoHeader().video_type_header); + int64_t unwrapped_tl0 = tl0_unwrapper_.Unwrap(codec_header.tl0PicIdx & 0xFF); + FrameDecision decision = + ManageFrameInternal(frame.get(), codec_header, unwrapped_tl0); RtpFrameReferenceFinder::ReturnVector res; switch (decision) { case kStash: - if (stashed_frames_.size() > kMaxStashedFrames) + if (stashed_frames_.size() > kMaxStashedFrames) { stashed_frames_.pop_back(); - stashed_frames_.push_front(std::move(frame)); + } + stashed_frames_.push_front( + {.unwrapped_tl0 = unwrapped_tl0, .frame = std::move(frame)}); return res; case kHandOff: res.push_back(std::move(frame)); @@ -39,11 +45,9 @@ RtpFrameReferenceFinder::ReturnVector RtpVp8RefFinder::ManageFrame( } RtpVp8RefFinder::FrameDecision RtpVp8RefFinder::ManageFrameInternal( - RtpFrameObject* frame) { - const RTPVideoHeader& video_header = frame->GetRtpVideoHeader(); - const RTPVideoHeaderVP8& codec_header = - absl::get(video_header.video_type_header); - + RtpFrameObject* frame, + const RTPVideoHeaderVP8& codec_header, + int64_t unwrapped_tl0) { // Protect against corrupted packets with arbitrary large temporal idx. if (codec_header.temporalIdx >= kMaxTemporalLayers) return kDrop; @@ -73,8 +77,6 @@ RtpVp8RefFinder::FrameDecision RtpVp8RefFinder::ManageFrameInternal( } while (last_picture_id_ != frame->Id()); } - int64_t unwrapped_tl0 = tl0_unwrapper_.Unwrap(codec_header.tl0PicIdx & 0xFF); - // Clean up info for base layers that are too old. int64_t old_tl0_pic_idx = unwrapped_tl0 - kMaxLayerInfo; auto clean_layer_info_to = layer_info_.lower_bound(old_tl0_pic_idx); @@ -207,20 +209,22 @@ void RtpVp8RefFinder::RetryStashedFrames( bool complete_frame = false; do { complete_frame = false; - for (auto frame_it = stashed_frames_.begin(); - frame_it != stashed_frames_.end();) { - FrameDecision decision = ManageFrameInternal(frame_it->get()); + for (auto it = stashed_frames_.begin(); it != stashed_frames_.end();) { + const RTPVideoHeaderVP8& codec_header = absl::get( + it->frame->GetRtpVideoHeader().video_type_header); + FrameDecision decision = + ManageFrameInternal(it->frame.get(), codec_header, it->unwrapped_tl0); switch (decision) { case kStash: - ++frame_it; + ++it; break; case kHandOff: complete_frame = true; - res.push_back(std::move(*frame_it)); + res.push_back(std::move(it->frame)); [[fallthrough]]; case kDrop: - frame_it = stashed_frames_.erase(frame_it); + it = stashed_frames_.erase(it); } } } while (complete_frame); @@ -235,7 +239,7 @@ void RtpVp8RefFinder::UnwrapPictureIds(RtpFrameObject* frame) { void RtpVp8RefFinder::ClearTo(uint16_t seq_num) { auto it = stashed_frames_.begin(); while (it != stashed_frames_.end()) { - if (AheadOf(seq_num, (*it)->first_seq_num())) { + if (AheadOf(seq_num, it->frame->first_seq_num())) { it = stashed_frames_.erase(it); } else { ++it; diff --git a/modules/video_coding/rtp_vp8_ref_finder.h b/modules/video_coding/rtp_vp8_ref_finder.h index 0a6cd7e10d..1ae45cdba3 100644 --- a/modules/video_coding/rtp_vp8_ref_finder.h +++ b/modules/video_coding/rtp_vp8_ref_finder.h @@ -38,9 +38,16 @@ class RtpVp8RefFinder { static constexpr int kMaxStashedFrames = 100; static constexpr int kMaxTemporalLayers = 5; + struct UnwrappedTl0Frame { + int64_t unwrapped_tl0; + std::unique_ptr frame; + }; + enum FrameDecision { kStash, kHandOff, kDrop }; - FrameDecision ManageFrameInternal(RtpFrameObject* frame); + FrameDecision ManageFrameInternal(RtpFrameObject* frame, + const RTPVideoHeaderVP8& codec_header, + int64_t unwrapped_tl0); void RetryStashedFrames(RtpFrameReferenceFinder::ReturnVector& res); void UpdateLayerInfoVp8(RtpFrameObject* frame, int64_t unwrapped_tl0, @@ -58,7 +65,7 @@ class RtpVp8RefFinder { // Frames that have been fully received but didn't have all the information // needed to determine their references. - std::deque> stashed_frames_; + std::deque stashed_frames_; // Holds the information about the last completed frame for a given temporal // layer given an unwrapped Tl0 picture index. diff --git a/modules/video_coding/rtp_vp8_ref_finder_unittest.cc b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc index a77149a89b..7dc6cd5521 100644 --- a/modules/video_coding/rtp_vp8_ref_finder_unittest.cc +++ b/modules/video_coding/rtp_vp8_ref_finder_unittest.cc @@ -357,4 +357,14 @@ TEST_F(RtpVp8RefFinderTest, Vp8DetectMissingFrame_0212) { EXPECT_THAT(frames_, HasFrameWithIdAndRefs(8, {5, 6, 7})); } +TEST_F(RtpVp8RefFinderTest, StashedFramesDoNotWrapTl0Backwards) { + Insert(Frame().Pid(0).Tid(0).Tl0(0)); + EXPECT_THAT(frames_, SizeIs(0)); + + Insert(Frame().Pid(128).Tid(0).Tl0(128).AsKeyFrame()); + EXPECT_THAT(frames_, SizeIs(1)); + Insert(Frame().Pid(129).Tid(0).Tl0(129)); + EXPECT_THAT(frames_, SizeIs(2)); +} + } // namespace webrtc diff --git a/modules/video_coding/rtp_vp9_ref_finder.cc b/modules/video_coding/rtp_vp9_ref_finder.cc index fd271f81f8..7a1f946904 100644 --- a/modules/video_coding/rtp_vp9_ref_finder.cc +++ b/modules/video_coding/rtp_vp9_ref_finder.cc @@ -16,17 +16,44 @@ #include "rtc_base/logging.h" namespace webrtc { - RtpFrameReferenceFinder::ReturnVector RtpVp9RefFinder::ManageFrame( std::unique_ptr frame) { - FrameDecision decision = ManageFrameInternal(frame.get()); + const RTPVideoHeaderVP9& codec_header = absl::get( + frame->GetRtpVideoHeader().video_type_header); + + frame->SetSpatialIndex(codec_header.spatial_idx); + frame->SetId(codec_header.picture_id & (kFrameIdLength - 1)); + + FrameDecision decision; + if (codec_header.temporal_idx >= kMaxTemporalLayers || + codec_header.spatial_idx >= kMaxSpatialLayers) { + decision = kDrop; + } else if (codec_header.flexible_mode) { + decision = ManageFrameFlexible(frame.get(), codec_header); + } else { + if (codec_header.tl0_pic_idx == kNoTl0PicIdx) { + RTC_LOG(LS_WARNING) << "TL0PICIDX is expected to be present in " + "non-flexible mode."; + decision = kDrop; + } else { + int64_t unwrapped_tl0 = + tl0_unwrapper_.Unwrap(codec_header.tl0_pic_idx & 0xFF); + decision = ManageFrameGof(frame.get(), codec_header, unwrapped_tl0); + + if (decision == kStash) { + if (stashed_frames_.size() > kMaxStashedFrames) { + stashed_frames_.pop_back(); + } + + stashed_frames_.push_front( + {.unwrapped_tl0 = unwrapped_tl0, .frame = std::move(frame)}); + } + } + } RtpFrameReferenceFinder::ReturnVector res; switch (decision) { case kStash: - if (stashed_frames_.size() > kMaxStashedFrames) - stashed_frames_.pop_back(); - stashed_frames_.push_front(std::move(frame)); return res; case kHandOff: res.push_back(std::move(frame)); @@ -39,46 +66,28 @@ RtpFrameReferenceFinder::ReturnVector RtpVp9RefFinder::ManageFrame( return res; } -RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameInternal( - RtpFrameObject* frame) { - const RTPVideoHeader& video_header = frame->GetRtpVideoHeader(); - const RTPVideoHeaderVP9& codec_header = - absl::get(video_header.video_type_header); - - // Protect against corrupted packets with arbitrary large temporal idx. - if (codec_header.temporal_idx >= kMaxTemporalLayers || - codec_header.spatial_idx >= kMaxSpatialLayers) +RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameFlexible( + RtpFrameObject* frame, + const RTPVideoHeaderVP9& codec_header) { + if (codec_header.num_ref_pics > EncodedFrame::kMaxFrameReferences) { return kDrop; - - frame->SetSpatialIndex(codec_header.spatial_idx); - frame->SetId(codec_header.picture_id & (kFrameIdLength - 1)); - - if (last_picture_id_ == -1) - last_picture_id_ = frame->Id(); - - if (codec_header.flexible_mode) { - if (codec_header.num_ref_pics > EncodedFrame::kMaxFrameReferences) { - return kDrop; - } - frame->num_references = codec_header.num_ref_pics; - for (size_t i = 0; i < frame->num_references; ++i) { - frame->references[i] = - Subtract(frame->Id(), codec_header.pid_diff[i]); - } - - FlattenFrameIdAndRefs(frame, codec_header.inter_layer_predicted); - return kHandOff; } - if (codec_header.tl0_pic_idx == kNoTl0PicIdx) { - RTC_LOG(LS_WARNING) << "TL0PICIDX is expected to be present in " - "non-flexible mode."; - return kDrop; + frame->num_references = codec_header.num_ref_pics; + for (size_t i = 0; i < frame->num_references; ++i) { + frame->references[i] = + Subtract(frame->Id(), codec_header.pid_diff[i]); } + FlattenFrameIdAndRefs(frame, codec_header.inter_layer_predicted); + return kHandOff; +} + +RtpVp9RefFinder::FrameDecision RtpVp9RefFinder::ManageFrameGof( + RtpFrameObject* frame, + const RTPVideoHeaderVP9& codec_header, + int64_t unwrapped_tl0) { GofInfo* info; - int64_t unwrapped_tl0 = - tl0_unwrapper_.Unwrap(codec_header.tl0_pic_idx & 0xFF); if (codec_header.ss_data_available) { if (codec_header.temporal_idx != 0) { RTC_LOG(LS_WARNING) << "Received scalability structure on a non base " @@ -303,20 +312,23 @@ void RtpVp9RefFinder::RetryStashedFrames( bool complete_frame = false; do { complete_frame = false; - for (auto frame_it = stashed_frames_.begin(); - frame_it != stashed_frames_.end();) { - FrameDecision decision = ManageFrameInternal(frame_it->get()); + for (auto it = stashed_frames_.begin(); it != stashed_frames_.end();) { + const RTPVideoHeaderVP9& codec_header = absl::get( + it->frame->GetRtpVideoHeader().video_type_header); + RTC_DCHECK(!codec_header.flexible_mode); + FrameDecision decision = + ManageFrameGof(it->frame.get(), codec_header, it->unwrapped_tl0); switch (decision) { case kStash: - ++frame_it; + ++it; break; case kHandOff: complete_frame = true; - res.push_back(std::move(*frame_it)); + res.push_back(std::move(it->frame)); [[fallthrough]]; case kDrop: - frame_it = stashed_frames_.erase(frame_it); + it = stashed_frames_.erase(it); } } } while (complete_frame); @@ -342,7 +354,7 @@ void RtpVp9RefFinder::FlattenFrameIdAndRefs(RtpFrameObject* frame, void RtpVp9RefFinder::ClearTo(uint16_t seq_num) { auto it = stashed_frames_.begin(); while (it != stashed_frames_.end()) { - if (AheadOf(seq_num, (*it)->first_seq_num())) { + if (AheadOf(seq_num, it->frame->first_seq_num())) { it = stashed_frames_.erase(it); } else { ++it; diff --git a/modules/video_coding/rtp_vp9_ref_finder.h b/modules/video_coding/rtp_vp9_ref_finder.h index 436cb1c84a..2971f686b1 100644 --- a/modules/video_coding/rtp_vp9_ref_finder.h +++ b/modules/video_coding/rtp_vp9_ref_finder.h @@ -48,7 +48,16 @@ class RtpVp9RefFinder { uint16_t last_picture_id; }; - FrameDecision ManageFrameInternal(RtpFrameObject* frame); + struct UnwrappedTl0Frame { + int64_t unwrapped_tl0; + std::unique_ptr frame; + }; + + FrameDecision ManageFrameFlexible(RtpFrameObject* frame, + const RTPVideoHeaderVP9& vp9_header); + FrameDecision ManageFrameGof(RtpFrameObject* frame, + const RTPVideoHeaderVP9& vp9_header, + int64_t unwrapped_tl0); void RetryStashedFrames(RtpFrameReferenceFinder::ReturnVector& res); bool MissingRequiredFrameVp9(uint16_t picture_id, const GofInfo& info); @@ -60,13 +69,9 @@ class RtpVp9RefFinder { void FlattenFrameIdAndRefs(RtpFrameObject* frame, bool inter_layer_predicted); - // Save the last picture id in order to detect when there is a gap in frames - // that have not yet been fully received. - int last_picture_id_ = -1; - // Frames that have been fully received but didn't have all the information // needed to determine their references. - std::deque> stashed_frames_; + std::deque stashed_frames_; // Where the current scalability structure is in the // `scalability_structures_` array. diff --git a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc index 6de7ce106f..66b284f020 100644 --- a/modules/video_coding/rtp_vp9_ref_finder_unittest.cc +++ b/modules/video_coding/rtp_vp9_ref_finder_unittest.cc @@ -23,6 +23,7 @@ using ::testing::Matches; using ::testing::MatchResultListener; using ::testing::Pointee; using ::testing::Property; +using ::testing::SizeIs; using ::testing::UnorderedElementsAreArray; namespace webrtc { @@ -702,4 +703,17 @@ TEST_F(RtpVp9RefFinderTest, SpatialIndex) { Contains(Pointee(Property(&EncodedFrame::SpatialIndex, 2)))); } +TEST_F(RtpVp9RefFinderTest, StashedFramesDoNotWrapTl0Backwards) { + GofInfoVP9 ss; + ss.SetGofInfoVP9(kTemporalStructureMode1); + + Insert(Frame().Pid(0).SidAndTid(0, 0).Tl0(0)); + EXPECT_THAT(frames_, SizeIs(0)); + + Insert(Frame().Pid(128).SidAndTid(0, 0).Tl0(128).AsKeyFrame().Gof(&ss)); + EXPECT_THAT(frames_, SizeIs(1)); + Insert(Frame().Pid(129).SidAndTid(0, 0).Tl0(129)); + EXPECT_THAT(frames_, SizeIs(2)); +} + } // namespace webrtc diff --git a/modules/video_coding/rtt_filter.cc b/modules/video_coding/rtt_filter.cc index 75813a46ad..eaf3b2b301 100644 --- a/modules/video_coding/rtt_filter.cc +++ b/modules/video_coding/rtt_filter.cc @@ -14,152 +14,148 @@ #include #include -#include "modules/video_coding/internal_defines.h" +#include + +#include "absl/algorithm/container.h" +#include "absl/container/inlined_vector.h" +#include "api/units/time_delta.h" namespace webrtc { +namespace { + +constexpr TimeDelta kMaxRtt = TimeDelta::Seconds(3); +constexpr uint32_t kFilterFactorMax = 35; +constexpr double kJumpStddev = 2.5; +constexpr double kDriftStdDev = 3.5; + +} // namespace + VCMRttFilter::VCMRttFilter() - : _filtFactMax(35), - _jumpStdDevs(2.5), - _driftStdDevs(3.5), - _detectThreshold(kMaxDriftJumpCount) { + : avg_rtt_(TimeDelta::Zero()), + var_rtt_(0), + max_rtt_(TimeDelta::Zero()), + jump_buf_(kMaxDriftJumpCount, TimeDelta::Zero()), + drift_buf_(kMaxDriftJumpCount, TimeDelta::Zero()) { Reset(); } -VCMRttFilter& VCMRttFilter::operator=(const VCMRttFilter& rhs) { - if (this != &rhs) { - _gotNonZeroUpdate = rhs._gotNonZeroUpdate; - _avgRtt = rhs._avgRtt; - _varRtt = rhs._varRtt; - _maxRtt = rhs._maxRtt; - _filtFactCount = rhs._filtFactCount; - _jumpCount = rhs._jumpCount; - _driftCount = rhs._driftCount; - memcpy(_jumpBuf, rhs._jumpBuf, sizeof(_jumpBuf)); - memcpy(_driftBuf, rhs._driftBuf, sizeof(_driftBuf)); - } - return *this; -} - void VCMRttFilter::Reset() { - _gotNonZeroUpdate = false; - _avgRtt = 0; - _varRtt = 0; - _maxRtt = 0; - _filtFactCount = 1; - _jumpCount = 0; - _driftCount = 0; - memset(_jumpBuf, 0, sizeof(_jumpBuf)); - memset(_driftBuf, 0, sizeof(_driftBuf)); + got_non_zero_update_ = false; + avg_rtt_ = TimeDelta::Zero(); + var_rtt_ = 0; + max_rtt_ = TimeDelta::Zero(); + filt_fact_count_ = 1; + absl::c_fill(jump_buf_, TimeDelta::Zero()); + absl::c_fill(drift_buf_, TimeDelta::Zero()); } -void VCMRttFilter::Update(int64_t rttMs) { - if (!_gotNonZeroUpdate) { - if (rttMs == 0) { +void VCMRttFilter::Update(TimeDelta rtt) { + if (!got_non_zero_update_) { + if (rtt.IsZero()) { return; } - _gotNonZeroUpdate = true; + got_non_zero_update_ = true; } // Sanity check - if (rttMs > 3000) { - rttMs = 3000; + if (rtt > kMaxRtt) { + rtt = kMaxRtt; } - double filtFactor = 0; - if (_filtFactCount > 1) { - filtFactor = static_cast(_filtFactCount - 1) / _filtFactCount; + double filt_factor = 0; + if (filt_fact_count_ > 1) { + filt_factor = static_cast(filt_fact_count_ - 1) / filt_fact_count_; } - _filtFactCount++; - if (_filtFactCount > _filtFactMax) { - // This prevents filtFactor from going above - // (_filtFactMax - 1) / _filtFactMax, - // e.g., _filtFactMax = 50 => filtFactor = 49/50 = 0.98 - _filtFactCount = _filtFactMax; + filt_fact_count_++; + if (filt_fact_count_ > kFilterFactorMax) { + // This prevents filt_factor from going above + // (_filt_fact_max - 1) / filt_fact_max_, + // e.g., filt_fact_max_ = 50 => filt_factor = 49/50 = 0.98 + filt_fact_count_ = kFilterFactorMax; } - double oldAvg = _avgRtt; - double oldVar = _varRtt; - _avgRtt = filtFactor * _avgRtt + (1 - filtFactor) * rttMs; - _varRtt = filtFactor * _varRtt + - (1 - filtFactor) * (rttMs - _avgRtt) * (rttMs - _avgRtt); - _maxRtt = VCM_MAX(rttMs, _maxRtt); - if (!JumpDetection(rttMs) || !DriftDetection(rttMs)) { + TimeDelta old_avg = avg_rtt_; + int64_t old_var = var_rtt_; + avg_rtt_ = filt_factor * avg_rtt_ + (1 - filt_factor) * rtt; + int64_t delta_ms = (rtt - avg_rtt_).ms(); + var_rtt_ = filt_factor * var_rtt_ + (1 - filt_factor) * (delta_ms * delta_ms); + max_rtt_ = std::max(rtt, max_rtt_); + if (!JumpDetection(rtt) || !DriftDetection(rtt)) { // In some cases we don't want to update the statistics - _avgRtt = oldAvg; - _varRtt = oldVar; + avg_rtt_ = old_avg; + var_rtt_ = old_var; } } -bool VCMRttFilter::JumpDetection(int64_t rttMs) { - double diffFromAvg = _avgRtt - rttMs; - if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt)) { - int diffSign = (diffFromAvg >= 0) ? 1 : -1; - int jumpCountSign = (_jumpCount >= 0) ? 1 : -1; - if (diffSign != jumpCountSign) { +bool VCMRttFilter::JumpDetection(TimeDelta rtt) { + TimeDelta diff_from_avg = avg_rtt_ - rtt; + // Unit of var_rtt_ is ms^2. + TimeDelta jump_threshold = TimeDelta::Millis(kJumpStddev * sqrt(var_rtt_)); + if (diff_from_avg.Abs() > jump_threshold) { + bool positive_diff = diff_from_avg >= TimeDelta::Zero(); + if (!jump_buf_.empty() && positive_diff != last_jump_positive_) { // Since the signs differ the samples currently // in the buffer is useless as they represent a // jump in a different direction. - _jumpCount = 0; + jump_buf_.clear(); } - if (abs(_jumpCount) < kMaxDriftJumpCount) { - // Update the buffer used for the short time - // statistics. + if (jump_buf_.size() < kMaxDriftJumpCount) { + // Update the buffer used for the short time statistics. // The sign of the diff is used for updating the counter since // we want to use the same buffer for keeping track of when // the RTT jumps down and up. - _jumpBuf[abs(_jumpCount)] = rttMs; - _jumpCount += diffSign; + jump_buf_.push_back(rtt); + last_jump_positive_ = positive_diff; } - if (abs(_jumpCount) >= _detectThreshold) { + if (jump_buf_.size() >= kMaxDriftJumpCount) { // Detected an RTT jump - ShortRttFilter(_jumpBuf, abs(_jumpCount)); - _filtFactCount = _detectThreshold + 1; - _jumpCount = 0; + ShortRttFilter(jump_buf_); + filt_fact_count_ = kMaxDriftJumpCount + 1; + jump_buf_.clear(); } else { return false; } } else { - _jumpCount = 0; + jump_buf_.clear(); } return true; } -bool VCMRttFilter::DriftDetection(int64_t rttMs) { - if (_maxRtt - _avgRtt > _driftStdDevs * sqrt(_varRtt)) { - if (_driftCount < kMaxDriftJumpCount) { - // Update the buffer used for the short time - // statistics. - _driftBuf[_driftCount] = rttMs; - _driftCount++; +bool VCMRttFilter::DriftDetection(TimeDelta rtt) { + // Unit of sqrt of var_rtt_ is ms. + TimeDelta drift_threshold = TimeDelta::Millis(kDriftStdDev * sqrt(var_rtt_)); + if (max_rtt_ - avg_rtt_ > drift_threshold) { + if (drift_buf_.size() < kMaxDriftJumpCount) { + // Update the buffer used for the short time statistics. + drift_buf_.push_back(rtt); } - if (_driftCount >= _detectThreshold) { + if (drift_buf_.size() >= kMaxDriftJumpCount) { // Detected an RTT drift - ShortRttFilter(_driftBuf, _driftCount); - _filtFactCount = _detectThreshold + 1; - _driftCount = 0; + ShortRttFilter(drift_buf_); + filt_fact_count_ = kMaxDriftJumpCount + 1; + drift_buf_.clear(); } } else { - _driftCount = 0; + drift_buf_.clear(); } return true; } -void VCMRttFilter::ShortRttFilter(int64_t* buf, uint32_t length) { - if (length == 0) { - return; - } - _maxRtt = 0; - _avgRtt = 0; - for (uint32_t i = 0; i < length; i++) { - if (buf[i] > _maxRtt) { - _maxRtt = buf[i]; +void VCMRttFilter::ShortRttFilter(const BufferList& buf) { + RTC_DCHECK_EQ(buf.size(), kMaxDriftJumpCount); + max_rtt_ = TimeDelta::Zero(); + avg_rtt_ = TimeDelta::Zero(); + for (const TimeDelta& rtt : buf) { + if (rtt > max_rtt_) { + max_rtt_ = rtt; } - _avgRtt += buf[i]; + avg_rtt_ += rtt; } - _avgRtt = _avgRtt / static_cast(length); + avg_rtt_ = avg_rtt_ / static_cast(buf.size()); } -int64_t VCMRttFilter::RttMs() const { - return static_cast(_maxRtt + 0.5); +TimeDelta VCMRttFilter::Rtt() const { + return max_rtt_; } + } // namespace webrtc diff --git a/modules/video_coding/rtt_filter.h b/modules/video_coding/rtt_filter.h index 073fabb85b..a611aafcb8 100644 --- a/modules/video_coding/rtt_filter.h +++ b/modules/video_coding/rtt_filter.h @@ -13,52 +13,55 @@ #include +#include "absl/container/inlined_vector.h" +#include "api/units/time_delta.h" + namespace webrtc { class VCMRttFilter { public: VCMRttFilter(); - - VCMRttFilter& operator=(const VCMRttFilter& rhs); + VCMRttFilter(const VCMRttFilter&) = delete; + VCMRttFilter& operator=(const VCMRttFilter&) = delete; // Resets the filter. void Reset(); // Updates the filter with a new sample. - void Update(int64_t rttMs); - // A getter function for the current RTT level in ms. - int64_t RttMs() const; + void Update(TimeDelta rtt); + // A getter function for the current RTT level. + TimeDelta Rtt() const; private: // The size of the drift and jump memory buffers // and thus also the detection threshold for these // detectors in number of samples. - enum { kMaxDriftJumpCount = 5 }; + static constexpr int kMaxDriftJumpCount = 5; + using BufferList = absl::InlinedVector; + // Detects RTT jumps by comparing the difference between // samples and average to the standard deviation. // Returns true if the long time statistics should be updated // and false otherwise - bool JumpDetection(int64_t rttMs); + bool JumpDetection(TimeDelta rtt); + // Detects RTT drifts by comparing the difference between // max and average to the standard deviation. // Returns true if the long time statistics should be updated // and false otherwise - bool DriftDetection(int64_t rttMs); + bool DriftDetection(TimeDelta rtt); + // Computes the short time average and maximum of the vector buf. - void ShortRttFilter(int64_t* buf, uint32_t length); + void ShortRttFilter(const BufferList& buf); - bool _gotNonZeroUpdate; - double _avgRtt; - double _varRtt; - int64_t _maxRtt; - uint32_t _filtFactCount; - const uint32_t _filtFactMax; - const double _jumpStdDevs; - const double _driftStdDevs; - int32_t _jumpCount; - int32_t _driftCount; - const int32_t _detectThreshold; - int64_t _jumpBuf[kMaxDriftJumpCount]; - int64_t _driftBuf[kMaxDriftJumpCount]; + bool got_non_zero_update_; + TimeDelta avg_rtt_; + // Variance units are TimeDelta^2. Store as ms^2. + int64_t var_rtt_; + TimeDelta max_rtt_; + uint32_t filt_fact_count_; + bool last_jump_positive_ = false; + BufferList jump_buf_; + BufferList drift_buf_; }; } // namespace webrtc diff --git a/modules/video_coding/rtt_filter_unittest.cc b/modules/video_coding/rtt_filter_unittest.cc new file mode 100644 index 0000000000..15d7d66b83 --- /dev/null +++ b/modules/video_coding/rtt_filter_unittest.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022 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/video_coding/rtt_filter.h" + +#include "api/units/time_delta.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(RttFilterTest, RttIsCapped) { + VCMRttFilter rtt_filter; + rtt_filter.Update(TimeDelta::Seconds(500)); + + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Seconds(3)); +} + +// If the difference between samples is more than away 2.5 stddev from the mean +// then this is considered a jump. After more than 5 data points at the new +// level, the RTT is reset to the new level. +TEST(RttFilterTest, PositiveJumpDetection) { + VCMRttFilter rtt_filter; + + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + + // Trigger 5 jumps. + rtt_filter.Update(TimeDelta::Millis(1400)); + rtt_filter.Update(TimeDelta::Millis(1500)); + rtt_filter.Update(TimeDelta::Millis(1600)); + rtt_filter.Update(TimeDelta::Millis(1600)); + + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600)); + + rtt_filter.Update(TimeDelta::Millis(1600)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1600)); +} + +TEST(RttFilterTest, NegativeJumpDetection) { + VCMRttFilter rtt_filter; + + for (int i = 0; i < 10; ++i) + rtt_filter.Update(TimeDelta::Millis(1500)); + + // Trigger 5 negative data points that jump rtt down. + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + // Before 5 data points at the new level, max RTT is still 1500. + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(1500)); + + rtt_filter.Update(TimeDelta::Millis(300)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(300)); +} + +TEST(RttFilterTest, JumpsResetByDirectionShift) { + VCMRttFilter rtt_filter; + for (int i = 0; i < 10; ++i) + rtt_filter.Update(TimeDelta::Millis(1500)); + + // Trigger 4 negative jumps, then a positive one. This resets the jump + // detection. + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(200)); + rtt_filter.Update(TimeDelta::Millis(2000)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000)); + + rtt_filter.Update(TimeDelta::Millis(300)); + EXPECT_EQ(rtt_filter.Rtt(), TimeDelta::Millis(2000)); +} + +// If the difference between the max and average is more than 3.5 stddevs away +// then a drift is detected, and a short filter is applied to find a new max +// rtt. +TEST(RttFilterTest, DriftDetection) { + VCMRttFilter rtt_filter; + + // Descend RTT by 30ms and settle at 700ms RTT. A drift is detected after rtt + // of 700ms is reported around 50 times for these targets. + constexpr TimeDelta kStartRtt = TimeDelta::Millis(1000); + constexpr TimeDelta kDriftTarget = TimeDelta::Millis(700); + constexpr TimeDelta kDelta = TimeDelta::Millis(30); + for (TimeDelta rtt = kStartRtt; rtt >= kDriftTarget; rtt -= kDelta) + rtt_filter.Update(rtt); + + EXPECT_EQ(rtt_filter.Rtt(), kStartRtt); + + for (int i = 0; i < 50; ++i) + rtt_filter.Update(kDriftTarget); + EXPECT_EQ(rtt_filter.Rtt(), kDriftTarget); +} + +} // namespace webrtc diff --git a/modules/video_coding/svc/BUILD.gn b/modules/video_coding/svc/BUILD.gn index 2eb25025c1..f68001ad72 100644 --- a/modules/video_coding/svc/BUILD.gn +++ b/modules/video_coding/svc/BUILD.gn @@ -8,6 +8,21 @@ import("../../../webrtc.gni") +rtc_source_set("scalability_mode_util") { + sources = [ + "scalability_mode_util.cc", + "scalability_mode_util.h", + ] + deps = [ + "../../../api/video_codecs:scalability_mode", + "../../../rtc_base:checks", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + rtc_source_set("scalable_video_controller") { sources = [ "scalable_video_controller.h", @@ -43,6 +58,7 @@ rtc_source_set("scalability_structures") { ":scalable_video_controller", "../../../api/transport/rtp:dependency_descriptor", "../../../api/video:video_bitrate_allocation", + "../../../api/video_codecs:scalability_mode", "../../../common_video/generic_frame_descriptor", "../../../rtc_base:checks", "../../../rtc_base:logging", @@ -75,6 +91,7 @@ if (rtc_include_tests) { rtc_source_set("scalability_structure_tests") { testonly = true sources = [ + "scalability_mode_util_unittest.cc", "scalability_structure_full_svc_unittest.cc", "scalability_structure_key_svc_unittest.cc", "scalability_structure_l2t2_key_shift_unittest.cc", @@ -83,6 +100,7 @@ if (rtc_include_tests) { "scalability_structure_unittest.cc", ] deps = [ + ":scalability_mode_util", ":scalability_structures", ":scalable_video_controller", "..:chain_diff_calculator", @@ -91,10 +109,14 @@ if (rtc_include_tests) { "../../../api/transport/rtp:dependency_descriptor", "../../../api/video:video_bitrate_allocation", "../../../api/video:video_frame_type", + "../../../api/video_codecs:scalability_mode", "../../../common_video/generic_frame_descriptor", "../../../test:test_support", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_source_set("svc_rate_allocator_tests") { diff --git a/modules/video_coding/svc/create_scalability_structure.cc b/modules/video_coding/svc/create_scalability_structure.cc index 8e5c06fca9..dfbfb14968 100644 --- a/modules/video_coding/svc/create_scalability_structure.cc +++ b/modules/video_coding/svc/create_scalability_structure.cc @@ -11,7 +11,7 @@ #include -#include "absl/strings/string_view.h" +#include "api/video_codecs/scalability_mode.h" #include "modules/video_coding/svc/scalability_structure_full_svc.h" #include "modules/video_coding/svc/scalability_structure_key_svc.h" #include "modules/video_coding/svc/scalability_structure_l2t2_key_shift.h" @@ -24,7 +24,7 @@ namespace webrtc { namespace { struct NamedStructureFactory { - absl::string_view name; + ScalabilityMode name; // Use function pointer to make NamedStructureFactory trivally destructable. std::unique_ptr (*factory)(); ScalableVideoController::StreamLayersConfig config; @@ -45,7 +45,7 @@ std::unique_ptr CreateH() { return std::make_unique(factor); } -constexpr ScalableVideoController::StreamLayersConfig kConfigNone = { +constexpr ScalableVideoController::StreamLayersConfig kConfigL1T1 = { /*num_spatial_layers=*/1, /*num_temporal_layers=*/1, /*uses_reference_scaling=*/false}; @@ -114,28 +114,33 @@ constexpr ScalableVideoController::StreamLayersConfig kConfigS3T3 = { {4, 2, 1}}; constexpr NamedStructureFactory kFactories[] = { - {"NONE", Create, kConfigNone}, - {"L1T2", Create, kConfigL1T2}, - {"L1T3", Create, kConfigL1T3}, - {"L2T1", Create, kConfigL2T1}, - {"L2T1h", CreateH, kConfigL2T1h}, - {"L2T1_KEY", Create, kConfigL2T1}, - {"L2T2", Create, kConfigL2T2}, - {"L2T2_KEY", Create, kConfigL2T2}, - {"L2T2_KEY_SHIFT", Create, kConfigL2T2}, - {"L2T3_KEY", Create, kConfigL2T3}, - {"L3T1", Create, kConfigL3T1}, - {"L3T3", Create, kConfigL3T3}, - {"L3T3_KEY", Create, kConfigL3T3}, - {"S2T1", Create, kConfigS2T1}, - {"S3T3", Create, kConfigS3T3}, + {ScalabilityMode::kL1T1, Create, + kConfigL1T1}, + {ScalabilityMode::kL1T2, Create, kConfigL1T2}, + {ScalabilityMode::kL1T3, Create, kConfigL1T3}, + {ScalabilityMode::kL2T1, Create, kConfigL2T1}, + {ScalabilityMode::kL2T1h, CreateH, kConfigL2T1h}, + {ScalabilityMode::kL2T1_KEY, Create, + kConfigL2T1}, + {ScalabilityMode::kL2T2, Create, kConfigL2T2}, + {ScalabilityMode::kL2T2_KEY, Create, + kConfigL2T2}, + {ScalabilityMode::kL2T2_KEY_SHIFT, Create, + kConfigL2T2}, + {ScalabilityMode::kL2T3_KEY, Create, + kConfigL2T3}, + {ScalabilityMode::kL3T1, Create, kConfigL3T1}, + {ScalabilityMode::kL3T3, Create, kConfigL3T3}, + {ScalabilityMode::kL3T3_KEY, Create, + kConfigL3T3}, + {ScalabilityMode::kS2T1, Create, kConfigS2T1}, + {ScalabilityMode::kS3T3, Create, kConfigS3T3}, }; } // namespace std::unique_ptr CreateScalabilityStructure( - absl::string_view name) { - RTC_DCHECK(!name.empty()); + ScalabilityMode name) { for (const auto& entry : kFactories) { if (entry.name == name) { return entry.factory(); @@ -145,8 +150,7 @@ std::unique_ptr CreateScalabilityStructure( } absl::optional -ScalabilityStructureConfig(absl::string_view name) { - RTC_DCHECK(!name.empty()); +ScalabilityStructureConfig(ScalabilityMode name) { for (const auto& entry : kFactories) { if (entry.name == name) { return entry.config; diff --git a/modules/video_coding/svc/create_scalability_structure.h b/modules/video_coding/svc/create_scalability_structure.h index fde034433e..3b67443693 100644 --- a/modules/video_coding/svc/create_scalability_structure.h +++ b/modules/video_coding/svc/create_scalability_structure.h @@ -13,8 +13,8 @@ #include #include -#include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "api/video_codecs/scalability_mode.h" #include "modules/video_coding/svc/scalable_video_controller.h" namespace webrtc { @@ -23,12 +23,12 @@ namespace webrtc { // https://w3c.github.io/webrtc-svc/#scalabilitymodes* // Returns nullptr for unknown name. std::unique_ptr CreateScalabilityStructure( - absl::string_view name); + ScalabilityMode name); -// Returns descrption of the scalability structure identified by 'name', +// Returns description of the scalability structure identified by 'name', // Return nullopt for unknown name. absl::optional -ScalabilityStructureConfig(absl::string_view name); +ScalabilityStructureConfig(ScalabilityMode name); } // namespace webrtc diff --git a/modules/video_coding/svc/scalability_mode_util.cc b/modules/video_coding/svc/scalability_mode_util.cc new file mode 100644 index 0000000000..20059017ec --- /dev/null +++ b/modules/video_coding/svc/scalability_mode_util.cc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2022 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/video_coding/svc/scalability_mode_util.h" + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/video_codecs/scalability_mode.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +absl::optional ScalabilityModeFromString( + absl::string_view mode_string) { + if (mode_string == "L1T1") + return ScalabilityMode::kL1T1; + if (mode_string == "L1T2") + return ScalabilityMode::kL1T2; + if (mode_string == "L1T2h") + return ScalabilityMode::kL1T2h; + if (mode_string == "L1T3") + return ScalabilityMode::kL1T3; + if (mode_string == "L1T3h") + return ScalabilityMode::kL1T3h; + + if (mode_string == "L2T1") + return ScalabilityMode::kL2T1; + if (mode_string == "L2T1h") + return ScalabilityMode::kL2T1h; + if (mode_string == "L2T1_KEY") + return ScalabilityMode::kL2T1_KEY; + + if (mode_string == "L2T2") + return ScalabilityMode::kL2T2; + if (mode_string == "L2T2h") + return ScalabilityMode::kL2T2h; + if (mode_string == "L2T2_KEY") + return ScalabilityMode::kL2T2_KEY; + if (mode_string == "L2T2_KEY_SHIFT") + return ScalabilityMode::kL2T2_KEY_SHIFT; + if (mode_string == "L2T3") + return ScalabilityMode::kL2T3; + if (mode_string == "L2T3h") + return ScalabilityMode::kL2T3h; + if (mode_string == "L2T3_KEY") + return ScalabilityMode::kL2T3_KEY; + + if (mode_string == "L3T1") + return ScalabilityMode::kL3T1; + if (mode_string == "L3T1h") + return ScalabilityMode::kL3T1h; + if (mode_string == "L3T1_KEY") + return ScalabilityMode::kL3T1_KEY; + + if (mode_string == "L3T2") + return ScalabilityMode::kL3T2; + if (mode_string == "L3T2h") + return ScalabilityMode::kL3T2h; + if (mode_string == "L3T2_KEY") + return ScalabilityMode::kL3T2_KEY; + + if (mode_string == "L3T3") + return ScalabilityMode::kL3T3; + if (mode_string == "L3T3h") + return ScalabilityMode::kL3T3h; + if (mode_string == "L3T3_KEY") + return ScalabilityMode::kL3T3_KEY; + + if (mode_string == "S2T1") + return ScalabilityMode::kS2T1; + if (mode_string == "S3T3") + return ScalabilityMode::kS3T3; + + return absl::nullopt; +} + +absl::string_view ScalabilityModeToString(ScalabilityMode scalability_mode) { + switch (scalability_mode) { + case ScalabilityMode::kL1T1: + return "L1T1"; + case ScalabilityMode::kL1T2: + return "L1T2"; + case ScalabilityMode::kL1T2h: + return "L1T2h"; + case ScalabilityMode::kL1T3: + return "L1T3"; + case ScalabilityMode::kL1T3h: + return "L1T3h"; + case ScalabilityMode::kL2T1: + return "L2T1"; + case ScalabilityMode::kL2T1h: + return "L2T1h"; + case ScalabilityMode::kL2T1_KEY: + return "L2T1_KEY"; + case ScalabilityMode::kL2T2: + return "L2T2"; + case ScalabilityMode::kL2T2h: + return "L2T2h"; + case ScalabilityMode::kL2T2_KEY: + return "L2T2_KEY"; + case ScalabilityMode::kL2T2_KEY_SHIFT: + return "L2T2_KEY_SHIFT"; + case ScalabilityMode::kL2T3: + return "L2T3"; + case ScalabilityMode::kL2T3h: + return "L2T3h"; + case ScalabilityMode::kL2T3_KEY: + return "L2T3_KEY"; + case ScalabilityMode::kL3T1: + return "L3T1"; + case ScalabilityMode::kL3T1h: + return "L3T1h"; + case ScalabilityMode::kL3T1_KEY: + return "L3T1_KEY"; + case ScalabilityMode::kL3T2: + return "L3T2"; + case ScalabilityMode::kL3T2h: + return "L3T2h"; + case ScalabilityMode::kL3T2_KEY: + return "L3T2_KEY"; + case ScalabilityMode::kL3T3: + return "L3T3"; + case ScalabilityMode::kL3T3h: + return "L3T3h"; + case ScalabilityMode::kL3T3_KEY: + return "L3T3_KEY"; + case ScalabilityMode::kS2T1: + return "S2T1"; + case ScalabilityMode::kS3T3: + return "S3T3"; + } + RTC_CHECK_NOTREACHED(); +} + +} // namespace webrtc diff --git a/modules/video_coding/svc/scalability_mode_util.h b/modules/video_coding/svc/scalability_mode_util.h new file mode 100644 index 0000000000..363cb6e6e4 --- /dev/null +++ b/modules/video_coding/svc/scalability_mode_util.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 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_VIDEO_CODING_SVC_SCALABILITY_MODE_UTIL_H_ +#define MODULES_VIDEO_CODING_SVC_SCALABILITY_MODE_UTIL_H_ + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/video_codecs/scalability_mode.h" + +namespace webrtc { + +absl::optional ScalabilityModeFromString( + absl::string_view scalability_mode_string); + +absl::string_view ScalabilityModeToString(ScalabilityMode scalability_mode); + +} // namespace webrtc + +#endif // MODULES_VIDEO_CODING_SVC_SCALABILITY_MODE_UTIL_H_ diff --git a/modules/video_coding/svc/scalability_mode_util_unittest.cc b/modules/video_coding/svc/scalability_mode_util_unittest.cc new file mode 100644 index 0000000000..7fb103631f --- /dev/null +++ b/modules/video_coding/svc/scalability_mode_util_unittest.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 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/video_coding/svc/scalability_mode_util.h" + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/video_codecs/scalability_mode.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(ScalabilityModeUtil, ConvertsL1T2) { + EXPECT_EQ(ScalabilityModeFromString("L1T2"), ScalabilityMode::kL1T2); + EXPECT_EQ(ScalabilityModeToString(ScalabilityMode::kL1T2), "L1T2"); +} + +TEST(ScalabilityModeUtil, RejectsUnknownString) { + EXPECT_EQ(ScalabilityModeFromString(""), absl::nullopt); + EXPECT_EQ(ScalabilityModeFromString("not-a-mode"), absl::nullopt); +} + +// Check roundtrip conversion of all enum values. +TEST(ScalabilityModeUtil, ConvertsAllToAndFromString) { + const ScalabilityMode kLastEnum = ScalabilityMode::kS3T3; + for (int numerical_enum = 0; numerical_enum <= static_cast(kLastEnum); + numerical_enum++) { + ScalabilityMode scalability_mode = + static_cast(numerical_enum); + absl::string_view scalability_mode_string = + ScalabilityModeToString(scalability_mode); + EXPECT_FALSE(scalability_mode_string.empty()); + EXPECT_EQ(ScalabilityModeFromString(scalability_mode_string), + scalability_mode); + } +} + +} // namespace +} // namespace webrtc diff --git a/modules/video_coding/svc/scalability_structure_unittest.cc b/modules/video_coding/svc/scalability_structure_unittest.cc index 9368f57db7..ffc085da04 100644 --- a/modules/video_coding/svc/scalability_structure_unittest.cc +++ b/modules/video_coding/svc/scalability_structure_unittest.cc @@ -19,6 +19,7 @@ #include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" #include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/svc/scalability_mode_util.h" #include "modules/video_coding/svc/scalability_structure_test_helpers.h" #include "modules/video_coding/svc/scalable_video_controller.h" #include "test/gmock.h" @@ -47,6 +48,13 @@ struct SvcTestParam { return os << param.name; } + ScalabilityMode GetScalabilityMode() const { + absl::optional scalability_mode = + ScalabilityModeFromString(name); + RTC_CHECK(scalability_mode.has_value()); + return *scalability_mode; + } + std::string name; int num_temporal_units; }; @@ -56,9 +64,9 @@ class ScalabilityStructureTest : public TestWithParam {}; TEST_P(ScalabilityStructureTest, StaticConfigMatchesConfigReturnedByController) { std::unique_ptr controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); absl::optional static_config = - ScalabilityStructureConfig(GetParam().name); + ScalabilityStructureConfig(GetParam().GetScalabilityMode()); ASSERT_THAT(controller, NotNull()); ASSERT_NE(static_config, absl::nullopt); ScalableVideoController::StreamLayersConfig config = @@ -78,7 +86,8 @@ TEST_P(ScalabilityStructureTest, TEST_P(ScalabilityStructureTest, NumberOfDecodeTargetsAndChainsAreInRangeAndConsistent) { FrameDependencyStructure structure = - CreateScalabilityStructure(GetParam().name)->DependencyStructure(); + CreateScalabilityStructure(GetParam().GetScalabilityMode()) + ->DependencyStructure(); EXPECT_GT(structure.num_decode_targets, 0); EXPECT_LE(structure.num_decode_targets, DependencyDescriptor::kMaxDecodeTargets); @@ -97,7 +106,8 @@ TEST_P(ScalabilityStructureTest, TEST_P(ScalabilityStructureTest, TemplatesAreSortedByLayerId) { FrameDependencyStructure structure = - CreateScalabilityStructure(GetParam().name)->DependencyStructure(); + CreateScalabilityStructure(GetParam().GetScalabilityMode()) + ->DependencyStructure(); ASSERT_THAT(structure.templates, Not(IsEmpty())); const auto& first_templates = structure.templates.front(); EXPECT_EQ(first_templates.spatial_id, 0); @@ -128,7 +138,8 @@ TEST_P(ScalabilityStructureTest, TemplatesAreSortedByLayerId) { TEST_P(ScalabilityStructureTest, TemplatesMatchNumberOfDecodeTargetsAndChains) { FrameDependencyStructure structure = - CreateScalabilityStructure(GetParam().name)->DependencyStructure(); + CreateScalabilityStructure(GetParam().GetScalabilityMode()) + ->DependencyStructure(); EXPECT_THAT( structure.templates, Each(AllOf(Field(&FrameDependencyTemplate::decode_target_indications, @@ -139,7 +150,7 @@ TEST_P(ScalabilityStructureTest, TemplatesMatchNumberOfDecodeTargetsAndChains) { TEST_P(ScalabilityStructureTest, FrameInfoMatchesFrameDependencyStructure) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); FrameDependencyStructure structure = svc_controller->DependencyStructure(); std::vector frame_infos = ScalabilityStructureWrapper(*svc_controller) @@ -158,7 +169,7 @@ TEST_P(ScalabilityStructureTest, FrameInfoMatchesFrameDependencyStructure) { TEST_P(ScalabilityStructureTest, ThereIsAPerfectTemplateForEachFrame) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); FrameDependencyStructure structure = svc_controller->DependencyStructure(); std::vector frame_infos = ScalabilityStructureWrapper(*svc_controller) @@ -171,7 +182,7 @@ TEST_P(ScalabilityStructureTest, ThereIsAPerfectTemplateForEachFrame) { TEST_P(ScalabilityStructureTest, FrameDependsOnSameOrLowerLayer) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); std::vector frame_infos = ScalabilityStructureWrapper(*svc_controller) .GenerateFrames(GetParam().num_temporal_units); @@ -192,7 +203,7 @@ TEST_P(ScalabilityStructureTest, FrameDependsOnSameOrLowerLayer) { TEST_P(ScalabilityStructureTest, NoFrameDependsOnDiscardableOrNotPresent) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); std::vector frame_infos = ScalabilityStructureWrapper(*svc_controller) .GenerateFrames(GetParam().num_temporal_units); @@ -224,7 +235,7 @@ TEST_P(ScalabilityStructureTest, NoFrameDependsOnDiscardableOrNotPresent) { TEST_P(ScalabilityStructureTest, NoFrameDependsThroughSwitchIndication) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); FrameDependencyStructure structure = svc_controller->DependencyStructure(); std::vector frame_infos = ScalabilityStructureWrapper(*svc_controller) @@ -277,7 +288,7 @@ TEST_P(ScalabilityStructureTest, NoFrameDependsThroughSwitchIndication) { TEST_P(ScalabilityStructureTest, ProduceNoFrameForDisabledLayers) { std::unique_ptr svc_controller = - CreateScalabilityStructure(GetParam().name); + CreateScalabilityStructure(GetParam().GetScalabilityMode()); ScalableVideoController::StreamLayersConfig structure = svc_controller->StreamConfig(); @@ -317,7 +328,7 @@ TEST_P(ScalabilityStructureTest, ProduceNoFrameForDisabledLayers) { INSTANTIATE_TEST_SUITE_P( Svc, ScalabilityStructureTest, - Values(SvcTestParam{"NONE", /*num_temporal_units=*/3}, + Values(SvcTestParam{"L1T1", /*num_temporal_units=*/3}, SvcTestParam{"L1T2", /*num_temporal_units=*/4}, SvcTestParam{"L1T3", /*num_temporal_units=*/8}, SvcTestParam{"L2T1", /*num_temporal_units=*/3}, diff --git a/modules/video_coding/svc/svc_rate_allocator.cc b/modules/video_coding/svc/svc_rate_allocator.cc index 2d27d47621..b6ae0d7430 100644 --- a/modules/video_coding/svc/svc_rate_allocator.cc +++ b/modules/video_coding/svc/svc_rate_allocator.cc @@ -174,8 +174,10 @@ DataRate FindLayerTogglingThreshold(const VideoCodec& codec, SvcRateAllocator::NumLayers SvcRateAllocator::GetNumLayers( const VideoCodec& codec) { NumLayers layers; - if (!codec.ScalabilityMode().empty()) { - if (auto structure = CreateScalabilityStructure(codec.ScalabilityMode())) { + if (absl::optional scalability_mode = + codec.GetScalabilityMode(); + scalability_mode.has_value()) { + if (auto structure = CreateScalabilityStructure(*scalability_mode)) { ScalableVideoController::StreamLayersConfig config = structure->StreamConfig(); layers.spatial = config.num_spatial_layers; diff --git a/modules/video_coding/svc/svc_rate_allocator_unittest.cc b/modules/video_coding/svc/svc_rate_allocator_unittest.cc index fd22acd85d..b3a365d722 100644 --- a/modules/video_coding/svc/svc_rate_allocator_unittest.cc +++ b/modules/video_coding/svc/svc_rate_allocator_unittest.cc @@ -275,7 +275,7 @@ TEST(SvcRateAllocatorTest, SupportsAv1) { codec.width = 640; codec.height = 360; codec.codecType = kVideoCodecAV1; - codec.SetScalabilityMode("L3T3"); + codec.SetScalabilityMode(ScalabilityMode::kL3T3); codec.spatialLayers[0].active = true; codec.spatialLayers[0].minBitrate = 30; codec.spatialLayers[0].targetBitrate = 51; @@ -304,7 +304,7 @@ TEST(SvcRateAllocatorTest, SupportsAv1WithSkippedLayer) { codec.width = 640; codec.height = 360; codec.codecType = kVideoCodecAV1; - codec.SetScalabilityMode("L3T3"); + codec.SetScalabilityMode(ScalabilityMode::kL3T3); codec.spatialLayers[0].active = false; codec.spatialLayers[0].minBitrate = 30; codec.spatialLayers[0].targetBitrate = 51; @@ -333,7 +333,7 @@ TEST(SvcRateAllocatorTest, UsesScalabilityModeToGetNumberOfLayers) { codec.width = 640; codec.height = 360; codec.codecType = kVideoCodecAV1; - codec.SetScalabilityMode("L2T2"); + codec.SetScalabilityMode(ScalabilityMode::kL2T2); codec.spatialLayers[0].active = true; codec.spatialLayers[0].minBitrate = 30; codec.spatialLayers[0].targetBitrate = 51; diff --git a/modules/video_coding/timing.cc b/modules/video_coding/timing.cc index df98416905..f45d620ae0 100644 --- a/modules/video_coding/timing.cc +++ b/modules/video_coding/timing.cc @@ -12,255 +12,227 @@ #include +#include "api/units/time_delta.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/time/timestamp_extrapolator.h" #include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { + // Default pacing that is used for the low-latency renderer path. constexpr TimeDelta kZeroPlayoutDelayDefaultMinPacing = TimeDelta::Millis(8); +constexpr TimeDelta kLowLatencyRendererMaxPlayoutDelay = TimeDelta::Millis(500); + } // namespace -VCMTiming::VCMTiming(Clock* clock) +VCMTiming::VCMTiming(Clock* clock, const FieldTrialsView& field_trials) : clock_(clock), - ts_extrapolator_(std::make_unique( - clock_->TimeInMilliseconds())), + ts_extrapolator_( + std::make_unique(clock_->CurrentTime())), codec_timer_(std::make_unique()), - render_delay_ms_(kDefaultRenderDelayMs), - min_playout_delay_ms_(0), - max_playout_delay_ms_(10000), - jitter_delay_ms_(0), - current_delay_ms_(0), + render_delay_(kDefaultRenderDelay), + min_playout_delay_(TimeDelta::Zero()), + max_playout_delay_(TimeDelta::Seconds(10)), + jitter_delay_(TimeDelta::Zero()), + current_delay_(TimeDelta::Zero()), prev_frame_timestamp_(0), - timing_frame_info_(), num_decoded_frames_(0), - low_latency_renderer_enabled_("enabled", true), zero_playout_delay_min_pacing_("min_pacing", kZeroPlayoutDelayDefaultMinPacing), - last_decode_scheduled_ts_(0) { - ParseFieldTrial({&low_latency_renderer_enabled_}, - field_trial::FindFullName("WebRTC-LowLatencyRenderer")); + last_decode_scheduled_(Timestamp::Zero()) { ParseFieldTrial({&zero_playout_delay_min_pacing_}, - field_trial::FindFullName("WebRTC-ZeroPlayoutDelay")); + field_trials.Lookup("WebRTC-ZeroPlayoutDelay")); } void VCMTiming::Reset() { MutexLock lock(&mutex_); - ts_extrapolator_->Reset(clock_->TimeInMilliseconds()); + ts_extrapolator_->Reset(clock_->CurrentTime()); codec_timer_ = std::make_unique(); - render_delay_ms_ = kDefaultRenderDelayMs; - min_playout_delay_ms_ = 0; - jitter_delay_ms_ = 0; - current_delay_ms_ = 0; + render_delay_ = kDefaultRenderDelay; + min_playout_delay_ = TimeDelta::Zero(); + jitter_delay_ = TimeDelta::Zero(); + current_delay_ = TimeDelta::Zero(); prev_frame_timestamp_ = 0; } -void VCMTiming::set_render_delay(int render_delay_ms) { +void VCMTiming::set_render_delay(TimeDelta render_delay) { MutexLock lock(&mutex_); - render_delay_ms_ = render_delay_ms; + render_delay_ = render_delay; } -void VCMTiming::set_min_playout_delay(int min_playout_delay_ms) { +void VCMTiming::set_min_playout_delay(TimeDelta min_playout_delay) { MutexLock lock(&mutex_); - min_playout_delay_ms_ = min_playout_delay_ms; + min_playout_delay_ = min_playout_delay; } -int VCMTiming::min_playout_delay() { +void VCMTiming::set_max_playout_delay(TimeDelta max_playout_delay) { MutexLock lock(&mutex_); - return min_playout_delay_ms_; + max_playout_delay_ = max_playout_delay; } -void VCMTiming::set_max_playout_delay(int max_playout_delay_ms) { +void VCMTiming::SetJitterDelay(TimeDelta jitter_delay) { MutexLock lock(&mutex_); - max_playout_delay_ms_ = max_playout_delay_ms; -} - -int VCMTiming::max_playout_delay() { - MutexLock lock(&mutex_); - return max_playout_delay_ms_; -} - -void VCMTiming::SetJitterDelay(int jitter_delay_ms) { - MutexLock lock(&mutex_); - if (jitter_delay_ms != jitter_delay_ms_) { - jitter_delay_ms_ = jitter_delay_ms; + if (jitter_delay != jitter_delay_) { + jitter_delay_ = jitter_delay; // When in initial state, set current delay to minimum delay. - if (current_delay_ms_ == 0) { - current_delay_ms_ = jitter_delay_ms_; + if (current_delay_.IsZero()) { + current_delay_ = jitter_delay_; } } } void VCMTiming::UpdateCurrentDelay(uint32_t frame_timestamp) { MutexLock lock(&mutex_); - int target_delay_ms = TargetDelayInternal(); + TimeDelta target_delay = TargetDelayInternal(); - if (current_delay_ms_ == 0) { + if (current_delay_.IsZero()) { // Not initialized, set current delay to target. - current_delay_ms_ = target_delay_ms; - } else if (target_delay_ms != current_delay_ms_) { - int64_t delay_diff_ms = - static_cast(target_delay_ms) - current_delay_ms_; + current_delay_ = target_delay; + } else if (target_delay != current_delay_) { + TimeDelta delay_diff = target_delay - current_delay_; // Never change the delay with more than 100 ms every second. If we're // changing the delay in too large steps we will get noticeable freezes. By // limiting the change we can increase the delay in smaller steps, which // will be experienced as the video is played in slow motion. When lowering // the delay the video will be played at a faster pace. - int64_t max_change_ms = 0; + TimeDelta max_change = TimeDelta::Zero(); if (frame_timestamp < 0x0000ffff && prev_frame_timestamp_ > 0xffff0000) { // wrap - max_change_ms = kDelayMaxChangeMsPerS * - (frame_timestamp + (static_cast(1) << 32) - - prev_frame_timestamp_) / - 90000; + max_change = + TimeDelta::Millis(kDelayMaxChangeMsPerS * + (frame_timestamp + (static_cast(1) << 32) - + prev_frame_timestamp_) / + 90000); } else { - max_change_ms = kDelayMaxChangeMsPerS * - (frame_timestamp - prev_frame_timestamp_) / 90000; + max_change = + TimeDelta::Millis(kDelayMaxChangeMsPerS * + (frame_timestamp - prev_frame_timestamp_) / 90000); } - if (max_change_ms <= 0) { + if (max_change <= TimeDelta::Zero()) { // Any changes less than 1 ms are truncated and will be postponed. // Negative change will be due to reordering and should be ignored. return; } - delay_diff_ms = std::max(delay_diff_ms, -max_change_ms); - delay_diff_ms = std::min(delay_diff_ms, max_change_ms); + delay_diff = std::max(delay_diff, -max_change); + delay_diff = std::min(delay_diff, max_change); - current_delay_ms_ = current_delay_ms_ + delay_diff_ms; + current_delay_ = current_delay_ + delay_diff; } prev_frame_timestamp_ = frame_timestamp; } -void VCMTiming::UpdateCurrentDelay(int64_t render_time_ms, - int64_t actual_decode_time_ms) { +void VCMTiming::UpdateCurrentDelay(Timestamp render_time, + Timestamp actual_decode_time) { MutexLock lock(&mutex_); - uint32_t target_delay_ms = TargetDelayInternal(); - int64_t delayed_ms = - actual_decode_time_ms - - (render_time_ms - RequiredDecodeTimeMs() - render_delay_ms_); - if (delayed_ms < 0) { + TimeDelta target_delay = TargetDelayInternal(); + TimeDelta delayed = + (actual_decode_time - render_time) + RequiredDecodeTime() + render_delay_; + if (delayed < TimeDelta::Zero()) { return; } - if (current_delay_ms_ + delayed_ms <= target_delay_ms) { - current_delay_ms_ += delayed_ms; + if (current_delay_ + delayed <= target_delay) { + current_delay_ += delayed; } else { - current_delay_ms_ = target_delay_ms; + current_delay_ = target_delay; } } -void VCMTiming::StopDecodeTimer(uint32_t /*time_stamp*/, - int32_t decode_time_ms, - int64_t now_ms, - int64_t /*render_time_ms*/) { - StopDecodeTimer(decode_time_ms, now_ms); -} - -void VCMTiming::StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms) { +void VCMTiming::StopDecodeTimer(TimeDelta decode_time, Timestamp now) { MutexLock lock(&mutex_); - codec_timer_->AddTiming(decode_time_ms, now_ms); - RTC_DCHECK_GE(decode_time_ms, 0); + codec_timer_->AddTiming(decode_time.ms(), now.ms()); + RTC_DCHECK_GE(decode_time, TimeDelta::Zero()); ++num_decoded_frames_; } -void VCMTiming::IncomingTimestamp(uint32_t time_stamp, int64_t now_ms) { +void VCMTiming::IncomingTimestamp(uint32_t rtp_timestamp, Timestamp now) { MutexLock lock(&mutex_); - ts_extrapolator_->Update(now_ms, time_stamp); + ts_extrapolator_->Update(now, rtp_timestamp); } -int64_t VCMTiming::RenderTimeMs(uint32_t frame_timestamp, - int64_t now_ms) const { +Timestamp VCMTiming::RenderTime(uint32_t frame_timestamp, Timestamp now) const { MutexLock lock(&mutex_); - return RenderTimeMsInternal(frame_timestamp, now_ms); + return RenderTimeInternal(frame_timestamp, now); } void VCMTiming::SetLastDecodeScheduledTimestamp( - int64_t last_decode_scheduled_ts) { + Timestamp last_decode_scheduled) { MutexLock lock(&mutex_); - last_decode_scheduled_ts_ = last_decode_scheduled_ts; + last_decode_scheduled_ = last_decode_scheduled; } -int64_t VCMTiming::RenderTimeMsInternal(uint32_t frame_timestamp, - int64_t now_ms) const { - constexpr int kLowLatencyRendererMaxPlayoutDelayMs = 500; - if (min_playout_delay_ms_ == 0 && - (max_playout_delay_ms_ == 0 || - (low_latency_renderer_enabled_ && - max_playout_delay_ms_ <= kLowLatencyRendererMaxPlayoutDelayMs))) { +Timestamp VCMTiming::RenderTimeInternal(uint32_t frame_timestamp, + Timestamp now) const { + if (min_playout_delay_.IsZero() && + (max_playout_delay_.IsZero() || + max_playout_delay_ <= kLowLatencyRendererMaxPlayoutDelay)) { // Render as soon as possible or with low-latency renderer algorithm. - return 0; + return Timestamp::Zero(); } // Note that TimestampExtrapolator::ExtrapolateLocalTime is not a const // method; it mutates the object's wraparound state. - int64_t estimated_complete_time_ms = - ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp); - if (estimated_complete_time_ms == -1) { - estimated_complete_time_ms = now_ms; - } - - // Make sure the actual delay stays in the range of `min_playout_delay_ms_` - // and `max_playout_delay_ms_`. - int actual_delay = std::max(current_delay_ms_, min_playout_delay_ms_); - actual_delay = std::min(actual_delay, max_playout_delay_ms_); - return estimated_complete_time_ms + actual_delay; + Timestamp estimated_complete_time = + ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp).value_or(now); + + // Make sure the actual delay stays in the range of `min_playout_delay_` + // and `max_playout_delay_`. + TimeDelta actual_delay = + current_delay_.Clamped(min_playout_delay_, max_playout_delay_); + return estimated_complete_time + actual_delay; } -int VCMTiming::RequiredDecodeTimeMs() const { +TimeDelta VCMTiming::RequiredDecodeTime() const { const int decode_time_ms = codec_timer_->RequiredDecodeTimeMs(); RTC_DCHECK_GE(decode_time_ms, 0); - return decode_time_ms; + return TimeDelta::Millis(decode_time_ms); } -int64_t VCMTiming::MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const { +TimeDelta VCMTiming::MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const { MutexLock lock(&mutex_); - if (render_time_ms == 0 && zero_playout_delay_min_pacing_->us() > 0 && - min_playout_delay_ms_ == 0 && max_playout_delay_ms_ > 0) { - // `render_time_ms` == 0 indicates that the frame should be decoded and + if (render_time.IsZero() && zero_playout_delay_min_pacing_->us() > 0 && + min_playout_delay_.IsZero() && max_playout_delay_ > TimeDelta::Zero()) { + // `render_time` == 0 indicates that the frame should be decoded and // rendered as soon as possible. However, the decoder can be choked if too // many frames are sent at once. Therefore, limit the interframe delay to // |zero_playout_delay_min_pacing_| unless too many frames are queued in // which case the frames are sent to the decoder at once. if (too_many_frames_queued) { - return 0; + return TimeDelta::Zero(); } - int64_t earliest_next_decode_start_time = - last_decode_scheduled_ts_ + zero_playout_delay_min_pacing_->ms(); - int64_t max_wait_time_ms = now_ms >= earliest_next_decode_start_time - ? 0 - : earliest_next_decode_start_time - now_ms; - return max_wait_time_ms; + Timestamp earliest_next_decode_start_time = + last_decode_scheduled_ + zero_playout_delay_min_pacing_; + TimeDelta max_wait_time = now >= earliest_next_decode_start_time + ? TimeDelta::Zero() + : earliest_next_decode_start_time - now; + return max_wait_time; } - return render_time_ms - now_ms - RequiredDecodeTimeMs() - render_delay_ms_; + return render_time - now - RequiredDecodeTime() - render_delay_; } -int VCMTiming::TargetVideoDelay() const { +TimeDelta VCMTiming::TargetVideoDelay() const { MutexLock lock(&mutex_); return TargetDelayInternal(); } -int VCMTiming::TargetDelayInternal() const { - return std::max(min_playout_delay_ms_, - jitter_delay_ms_ + RequiredDecodeTimeMs() + render_delay_ms_); +TimeDelta VCMTiming::TargetDelayInternal() const { + return std::max(min_playout_delay_, + jitter_delay_ + RequiredDecodeTime() + render_delay_); } -bool VCMTiming::GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const { +VCMTiming::VideoDelayTimings VCMTiming::GetTimings() const { MutexLock lock(&mutex_); - *max_decode_ms = RequiredDecodeTimeMs(); - *current_delay_ms = current_delay_ms_; - *target_delay_ms = TargetDelayInternal(); - *jitter_buffer_ms = jitter_delay_ms_; - *min_playout_delay_ms = min_playout_delay_ms_; - *render_delay_ms = render_delay_ms_; - return (num_decoded_frames_ > 0); + return VideoDelayTimings{.max_decode_duration = RequiredDecodeTime(), + .current_delay = current_delay_, + .target_delay = TargetDelayInternal(), + .jitter_buffer_delay = jitter_delay_, + .min_playout_delay = min_playout_delay_, + .max_playout_delay = max_playout_delay_, + .render_delay = render_delay_, + .num_decoded_frames = num_decoded_frames_}; } void VCMTiming::SetTimingFrameInfo(const TimingFrameInfo& info) { diff --git a/modules/video_coding/timing.h b/modules/video_coding/timing.h index 07c12e919a..41a6f87bde 100644 --- a/modules/video_coding/timing.h +++ b/modules/video_coding/timing.h @@ -14,6 +14,7 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/units/time_delta.h" #include "api/video/video_timing.h" #include "modules/video_coding/codec_timer.h" @@ -29,26 +30,27 @@ class TimestampExtrapolator; class VCMTiming { public: - explicit VCMTiming(Clock* clock); + static constexpr auto kDefaultRenderDelay = TimeDelta::Millis(10); + static constexpr auto kDelayMaxChangeMsPerS = 100; + + VCMTiming(Clock* clock, const FieldTrialsView& field_trials); virtual ~VCMTiming() = default; // Resets the timing to the initial state. void Reset(); // Set the amount of time needed to render an image. Defaults to 10 ms. - void set_render_delay(int render_delay_ms); + void set_render_delay(TimeDelta render_delay); // Set the minimum time the video must be delayed on the receiver to // get the desired jitter buffer level. - void SetJitterDelay(int required_delay_ms); + void SetJitterDelay(TimeDelta required_delay); - // Set/get the minimum playout delay from capture to render in ms. - void set_min_playout_delay(int min_playout_delay_ms); - int min_playout_delay(); + // Set/get the minimum playout delay from capture to render. + void set_min_playout_delay(TimeDelta min_playout_delay); // Set/get the maximum playout delay from capture to render in ms. - void set_max_playout_delay(int max_playout_delay_ms); - int max_playout_delay(); + void set_max_playout_delay(TimeDelta max_playout_delay); // Increases or decreases the current delay to get closer to the target delay. // Calculates how long it has been since the previous call to this function, @@ -59,51 +61,49 @@ class VCMTiming { // Given the actual decode time in ms and the render time in ms for a frame, // this function calculates how late the frame is and increases the delay // accordingly. - void UpdateCurrentDelay(int64_t render_time_ms, - int64_t actual_decode_time_ms); + void UpdateCurrentDelay(Timestamp render_time, Timestamp actual_decode_time); // Stops the decoder timer, should be called when the decoder returns a frame // or when the decoded frame callback is called. - void StopDecodeTimer(int32_t decode_time_ms, int64_t now_ms); - // TODO(kron): Remove once downstream projects has been changed to use the - // above function. - void StopDecodeTimer(uint32_t time_stamp, - int32_t decode_time_ms, - int64_t now_ms, - int64_t render_time_ms); + void StopDecodeTimer(TimeDelta decode_time, Timestamp now); // Used to report that a frame is passed to decoding. Updates the timestamp // filter which is used to map between timestamps and receiver system time. - void IncomingTimestamp(uint32_t time_stamp, int64_t last_packet_time_ms); + void IncomingTimestamp(uint32_t rtp_timestamp, Timestamp last_packet_time); // Returns the receiver system time when the frame with timestamp // `frame_timestamp` should be rendered, assuming that the system time - // currently is `now_ms`. - virtual int64_t RenderTimeMs(uint32_t frame_timestamp, int64_t now_ms) const; + // currently is `now`. + virtual Timestamp RenderTime(uint32_t frame_timestamp, Timestamp now) const; // Returns the maximum time in ms that we can wait for a frame to become - // complete before we must pass it to the decoder. render_time_ms==0 indicates + // complete before we must pass it to the decoder. render_time==0 indicates // that the frames should be processed as quickly as possible, with possibly // only a small delay added to make sure that the decoder is not overloaded. // In this case, the parameter too_many_frames_queued is used to signal that // the decode queue is full and that the frame should be decoded as soon as // possible. - virtual int64_t MaxWaitingTime(int64_t render_time_ms, - int64_t now_ms, - bool too_many_frames_queued) const; + virtual TimeDelta MaxWaitingTime(Timestamp render_time, + Timestamp now, + bool too_many_frames_queued) const; // Returns the current target delay which is required delay + decode time + // render delay. - int TargetVideoDelay() const; + TimeDelta TargetVideoDelay() const; // Return current timing information. Returns true if the first frame has been // decoded, false otherwise. - virtual bool GetTimings(int* max_decode_ms, - int* current_delay_ms, - int* target_delay_ms, - int* jitter_buffer_ms, - int* min_playout_delay_ms, - int* render_delay_ms) const; + struct VideoDelayTimings { + TimeDelta max_decode_duration; + TimeDelta current_delay; + TimeDelta target_delay; + TimeDelta jitter_buffer_delay; + TimeDelta min_playout_delay; + TimeDelta max_playout_delay; + TimeDelta render_delay; + size_t num_decoded_frames; + }; + VideoDelayTimings GetTimings() const; void SetTimingFrameInfo(const TimingFrameInfo& info); absl::optional GetTimingFrameInfo(); @@ -113,16 +113,13 @@ class VCMTiming { absl::optional MaxCompositionDelayInFrames() const; // Updates the last time a frame was scheduled for decoding. - void SetLastDecodeScheduledTimestamp(int64_t last_decode_scheduled_ts); - - enum { kDefaultRenderDelayMs = 10 }; - enum { kDelayMaxChangeMsPerS = 100 }; + void SetLastDecodeScheduledTimestamp(Timestamp last_decode_scheduled); protected: - int RequiredDecodeTimeMs() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - int64_t RenderTimeMsInternal(uint32_t frame_timestamp, int64_t now_ms) const + TimeDelta RequiredDecodeTime() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + Timestamp RenderTimeInternal(uint32_t frame_timestamp, Timestamp now) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - int TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + TimeDelta TargetDelayInternal() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); private: mutable Mutex mutex_; @@ -131,24 +128,19 @@ class VCMTiming { RTC_PT_GUARDED_BY(mutex_); std::unique_ptr codec_timer_ RTC_GUARDED_BY(mutex_) RTC_PT_GUARDED_BY(mutex_); - int render_delay_ms_ RTC_GUARDED_BY(mutex_); + TimeDelta render_delay_ RTC_GUARDED_BY(mutex_); // Best-effort playout delay range for frames from capture to render. // The receiver tries to keep the delay between `min_playout_delay_ms_` // and `max_playout_delay_ms_` taking the network jitter into account. // A special case is where min_playout_delay_ms_ = max_playout_delay_ms_ = 0, // in which case the receiver tries to play the frames as they arrive. - int min_playout_delay_ms_ RTC_GUARDED_BY(mutex_); - int max_playout_delay_ms_ RTC_GUARDED_BY(mutex_); - int jitter_delay_ms_ RTC_GUARDED_BY(mutex_); - int current_delay_ms_ RTC_GUARDED_BY(mutex_); + TimeDelta min_playout_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta max_playout_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta jitter_delay_ RTC_GUARDED_BY(mutex_); + TimeDelta current_delay_ RTC_GUARDED_BY(mutex_); uint32_t prev_frame_timestamp_ RTC_GUARDED_BY(mutex_); absl::optional timing_frame_info_ RTC_GUARDED_BY(mutex_); size_t num_decoded_frames_ RTC_GUARDED_BY(mutex_); - // Set by the field trial WebRTC-LowLatencyRenderer. The parameter enabled - // determines if the low-latency renderer algorithm should be used for the - // case min playout delay=0 and max playout delay>0. - FieldTrialParameter low_latency_renderer_enabled_ - RTC_GUARDED_BY(mutex_); absl::optional max_composition_delay_in_frames_ RTC_GUARDED_BY(mutex_); // Set by the field trial WebRTC-ZeroPlayoutDelay. The parameter min_pacing // determines the minimum delay between frames scheduled for decoding that is @@ -158,7 +150,7 @@ class VCMTiming { // Timestamp at which the last frame was scheduled to be sent to the decoder. // Used only when the RTP header extension playout delay is set to min=0 ms // which is indicated by a render time set to 0. - int64_t last_decode_scheduled_ts_ RTC_GUARDED_BY(mutex_); + Timestamp last_decode_scheduled_ RTC_GUARDED_BY(mutex_); }; } // namespace webrtc diff --git a/modules/video_coding/timing_unittest.cc b/modules/video_coding/timing_unittest.cc index 71de1fe3da..20667c9db1 100644 --- a/modules/video_coding/timing_unittest.cc +++ b/modules/video_coding/timing_unittest.cc @@ -10,18 +10,24 @@ #include "modules/video_coding/timing.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" #include "system_wrappers/include/clock.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace { -const int kFps = 25; + +constexpr Frequency k25Fps = Frequency::Hertz(25); +constexpr Frequency k90kHz = Frequency::KiloHertz(90); + } // namespace TEST(ReceiverTimingTest, JitterDelay) { + test::ScopedKeyValueConfig field_trials; SimulatedClock clock(0); - VCMTiming timing(&clock); + VCMTiming timing(&clock, field_trials); timing.Reset(); uint32_t timestamp = 0; @@ -29,102 +35,106 @@ TEST(ReceiverTimingTest, JitterDelay) { timing.Reset(); - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); - uint32_t jitter_delay_ms = 20; - timing.SetJitterDelay(jitter_delay_ms); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); + TimeDelta jitter_delay = TimeDelta::Millis(20); + timing.SetJitterDelay(jitter_delay); timing.UpdateCurrentDelay(timestamp); - timing.set_render_delay(0); - uint32_t wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + timing.set_render_delay(TimeDelta::Zero()); + auto wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // First update initializes the render time. Since we have no decode delay - // we get wait_time_ms = renderTime - now - renderDelay = jitter. - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + // we get wait_time = renderTime - now - renderDelay = jitter. + EXPECT_EQ(jitter_delay, wait_time); - jitter_delay_ms += VCMTiming::kDelayMaxChangeMsPerS + 10; + jitter_delay += TimeDelta::Millis(VCMTiming::kDelayMaxChangeMsPerS + 10); timestamp += 90000; clock.AdvanceTimeMilliseconds(1000); - timing.SetJitterDelay(jitter_delay_ms); + timing.SetJitterDelay(jitter_delay); timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // Since we gradually increase the delay we only get 100 ms every second. - EXPECT_EQ(jitter_delay_ms - 10, wait_time_ms); + EXPECT_EQ(jitter_delay - TimeDelta::Millis(10), wait_time); timestamp += 90000; clock.AdvanceTimeMilliseconds(1000); timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); // Insert frames without jitter, verify that this gives the exact wait time. const int kNumFrames = 300; for (int i = 0; i < kNumFrames; i++) { - clock.AdvanceTimeMilliseconds(1000 / kFps); - timestamp += 90000 / kFps; - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); + clock.AdvanceTime(1 / k25Fps); + timestamp += k90kHz / k25Fps; + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); } timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); // Add decode time estimates for 1 second. - const uint32_t kDecodeTimeMs = 10; - for (int i = 0; i < kFps; i++) { - clock.AdvanceTimeMilliseconds(kDecodeTimeMs); - timing.StopDecodeTimer(kDecodeTimeMs, clock.TimeInMilliseconds()); - timestamp += 90000 / kFps; - clock.AdvanceTimeMilliseconds(1000 / kFps - kDecodeTimeMs); - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); + const TimeDelta kDecodeTime = TimeDelta::Millis(10); + for (int i = 0; i < k25Fps.hertz(); i++) { + clock.AdvanceTime(kDecodeTime); + timing.StopDecodeTimer(kDecodeTime, clock.CurrentTime()); + timestamp += k90kHz / k25Fps; + clock.AdvanceTime(1 / k25Fps - kDecodeTime); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); } timing.UpdateCurrentDelay(timestamp); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); - EXPECT_EQ(jitter_delay_ms, wait_time_ms); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); + EXPECT_EQ(jitter_delay, wait_time); - const int kMinTotalDelayMs = 200; - timing.set_min_playout_delay(kMinTotalDelayMs); + const TimeDelta kMinTotalDelay = TimeDelta::Millis(200); + timing.set_min_playout_delay(kMinTotalDelay); clock.AdvanceTimeMilliseconds(5000); timestamp += 5 * 90000; timing.UpdateCurrentDelay(timestamp); - const int kRenderDelayMs = 10; - timing.set_render_delay(kRenderDelayMs); - wait_time_ms = timing.MaxWaitingTime( - timing.RenderTimeMs(timestamp, clock.TimeInMilliseconds()), - clock.TimeInMilliseconds(), /*too_many_frames_queued=*/false); + const TimeDelta kRenderDelay = TimeDelta::Millis(10); + timing.set_render_delay(kRenderDelay); + wait_time = timing.MaxWaitingTime( + timing.RenderTime(timestamp, clock.CurrentTime()), clock.CurrentTime(), + /*too_many_frames_queued=*/false); // We should at least have kMinTotalDelayMs - decodeTime (10) - renderTime // (10) to wait. - EXPECT_EQ(kMinTotalDelayMs - kDecodeTimeMs - kRenderDelayMs, wait_time_ms); + EXPECT_EQ(kMinTotalDelay - kDecodeTime - kRenderDelay, wait_time); // The total video delay should be equal to the min total delay. - EXPECT_EQ(kMinTotalDelayMs, timing.TargetVideoDelay()); + EXPECT_EQ(kMinTotalDelay, timing.TargetVideoDelay()); // Reset playout delay. - timing.set_min_playout_delay(0); + timing.set_min_playout_delay(TimeDelta::Zero()); clock.AdvanceTimeMilliseconds(5000); timestamp += 5 * 90000; timing.UpdateCurrentDelay(timestamp); } TEST(ReceiverTimingTest, TimestampWrapAround) { - SimulatedClock clock(0); - VCMTiming timing(&clock); + constexpr auto kStartTime = Timestamp::Millis(1337); + test::ScopedKeyValueConfig field_trials; + SimulatedClock clock(kStartTime); + VCMTiming timing(&clock, field_trials); + // Provoke a wrap-around. The fifth frame will have wrapped at 25 fps. - uint32_t timestamp = 0xFFFFFFFFu - 3 * 90000 / kFps; + constexpr uint32_t kRtpTicksPerFrame = k90kHz / k25Fps; + uint32_t timestamp = 0xFFFFFFFFu - 3 * kRtpTicksPerFrame; for (int i = 0; i < 5; ++i) { - timing.IncomingTimestamp(timestamp, clock.TimeInMilliseconds()); - clock.AdvanceTimeMilliseconds(1000 / kFps); - timestamp += 90000 / kFps; - EXPECT_EQ(3 * 1000 / kFps, - timing.RenderTimeMs(0xFFFFFFFFu, clock.TimeInMilliseconds())); - EXPECT_EQ(3 * 1000 / kFps + 1, - timing.RenderTimeMs(89u, // One ms later in 90 kHz. - clock.TimeInMilliseconds())); + timing.IncomingTimestamp(timestamp, clock.CurrentTime()); + clock.AdvanceTime(1 / k25Fps); + timestamp += kRtpTicksPerFrame; + EXPECT_EQ(kStartTime + 3 / k25Fps, + timing.RenderTime(0xFFFFFFFFu, clock.CurrentTime())); + // One ms later in 90 kHz. + EXPECT_EQ(kStartTime + 3 / k25Fps + TimeDelta::Millis(1), + timing.RenderTime(89u, clock.CurrentTime())); } } @@ -132,152 +142,153 @@ TEST(ReceiverTimingTest, MaxWaitingTimeIsZeroForZeroRenderTime) { // This is the default path when the RTP playout delay header extension is set // to min==0 and max==0. constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + constexpr TimeDelta kTimeDelta = 1 / Frequency::Hertz(60); + constexpr Timestamp kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); - VCMTiming timing(&clock); + test::ScopedKeyValueConfig field_trials; + VCMTiming timing(&clock, field_trials); timing.Reset(); - timing.set_max_playout_delay(0); + timing.set_max_playout_delay(TimeDelta::Zero()); for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + Timestamp now = clock.CurrentTime(); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); } // Another frame submitted at the same time also returns a negative max // waiting time. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + Timestamp now = clock.CurrentTime(); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); // MaxWaitingTime should be less than zero even if there's a burst of frames. - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_LT(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); + TimeDelta::Zero()); } TEST(ReceiverTimingTest, MaxWaitingTimeZeroDelayPacingExperiment) { // The minimum pacing is enabled by a field trial and active if the RTP // playout delay header extension is set to min==0. - constexpr int64_t kMinPacingMs = 3; - test::ScopedFieldTrials override_field_trials( + constexpr TimeDelta kMinPacing = TimeDelta::Millis(3); + test::ScopedKeyValueConfig field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + constexpr TimeDelta kTimeDelta = 1 / Frequency::Hertz(60); + constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); - VCMTiming timing(&clock); + VCMTiming timing(&clock, field_trials); timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + Timestamp now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - timing.SetLastDecodeScheduledTimestamp(now_ms); + TimeDelta::Zero()); + timing.SetLastDecodeScheduledTimestamp(now); } // Another frame submitted at the same time is paced according to the field // trial setting. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + auto now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // If there's a burst of frames, the wait time is calculated based on next // decode time. - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + kMinPacing); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // Allow a few ms to pass, this should be subtracted from the MaxWaitingTime. - constexpr int64_t kTwoMs = 2; - clock.AdvanceTimeMilliseconds(kTwoMs); - now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + constexpr TimeDelta kTwoMs = TimeDelta::Millis(2); + clock.AdvanceTime(kTwoMs); + now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs - kTwoMs); + kMinPacing - kTwoMs); // A frame is decoded at the current time, the wait time should be restored to // pacing delay. - timing.SetLastDecodeScheduledTimestamp(now_ms); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + timing.SetLastDecodeScheduledTimestamp(now); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); } TEST(ReceiverTimingTest, DefaultMaxWaitingTimeUnaffectedByPacingExperiment) { // The minimum pacing is enabled by a field trial but should not have any // effect if render_time_ms is greater than 0; - test::ScopedFieldTrials override_field_trials( + test::ScopedKeyValueConfig field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; + const TimeDelta kTimeDelta = TimeDelta::Millis(1000.0 / 60.0); SimulatedClock clock(kStartTimeUs); - VCMTiming timing(&clock); + VCMTiming timing(&clock, field_trials); timing.Reset(); - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - int64_t render_time_ms = now_ms + 30; + clock.AdvanceTime(kTimeDelta); + auto now = clock.CurrentTime(); + Timestamp render_time = now + TimeDelta::Millis(30); // Estimate the internal processing delay from the first frame. - int64_t estimated_processing_delay = - (render_time_ms - now_ms) - - timing.MaxWaitingTime(render_time_ms, now_ms, + TimeDelta estimated_processing_delay = + (render_time - now) - + timing.MaxWaitingTime(render_time, now, /*too_many_frames_queued=*/false); - EXPECT_GT(estimated_processing_delay, 0); + EXPECT_GT(estimated_processing_delay, TimeDelta::Zero()); // Any other frame submitted at the same time should be scheduled according to // its render time. for (int i = 0; i < 5; ++i) { - render_time_ms += kTimeDeltaMs; - EXPECT_EQ(timing.MaxWaitingTime(render_time_ms, now_ms, + render_time += kTimeDelta; + EXPECT_EQ(timing.MaxWaitingTime(render_time, now, /*too_many_frames_queued=*/false), - render_time_ms - now_ms - estimated_processing_delay); + render_time - now - estimated_processing_delay); } } -TEST(ReceiverTiminTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { +TEST(ReceiverTimingTest, MaxWaitingTimeReturnsZeroIfTooManyFramesQueuedIsTrue) { // The minimum pacing is enabled by a field trial and active if the RTP // playout delay header extension is set to min==0. - constexpr int64_t kMinPacingMs = 3; - test::ScopedFieldTrials override_field_trials( + constexpr TimeDelta kMinPacing = TimeDelta::Millis(3); + test::ScopedKeyValueConfig field_trials( "WebRTC-ZeroPlayoutDelay/min_pacing:3ms/"); constexpr int64_t kStartTimeUs = 3.15e13; // About one year in us. - constexpr int64_t kTimeDeltaMs = 1000.0 / 60.0; - constexpr int64_t kZeroRenderTimeMs = 0; + const TimeDelta kTimeDelta = TimeDelta::Millis(1000.0 / 60.0); + constexpr auto kZeroRenderTime = Timestamp::Zero(); SimulatedClock clock(kStartTimeUs); - VCMTiming timing(&clock); + VCMTiming timing(&clock, field_trials); timing.Reset(); // MaxWaitingTime() returns zero for evenly spaced video frames. for (int i = 0; i < 10; ++i) { - clock.AdvanceTimeMilliseconds(kTimeDeltaMs); - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + clock.AdvanceTime(kTimeDelta); + auto now = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now, /*too_many_frames_queued=*/false), - 0); - timing.SetLastDecodeScheduledTimestamp(now_ms); + TimeDelta::Zero()); + timing.SetLastDecodeScheduledTimestamp(now); } // Another frame submitted at the same time is paced according to the field // trial setting. - int64_t now_ms = clock.TimeInMilliseconds(); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + auto now_ms = clock.CurrentTime(); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/false), - kMinPacingMs); + kMinPacing); // MaxWaitingTime returns 0 even if there's a burst of frames if // too_many_frames_queued is set to true. - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/true), - 0); - EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTimeMs, now_ms, + TimeDelta::Zero()); + EXPECT_EQ(timing.MaxWaitingTime(kZeroRenderTime, now_ms, /*too_many_frames_queued=*/true), - 0); + TimeDelta::Zero()); } } // namespace webrtc diff --git a/modules/video_coding/video_codec_initializer.cc b/modules/video_coding/video_codec_initializer.cc index 5d06a2c133..80b9dc98f8 100644 --- a/modules/video_coding/video_codec_initializer.cc +++ b/modules/video_coding/video_codec_initializer.cc @@ -94,7 +94,8 @@ VideoCodec VideoCodecInitializer::VideoEncoderConfigToVideoCodec( int max_framerate = 0; - absl::optional scalability_mode = streams[0].scalability_mode; + absl::optional scalability_mode = + streams[0].scalability_mode; for (size_t i = 0; i < streams.size(); ++i) { SpatialLayer* sim_stream = &video_codec.simulcastStream[i]; RTC_DCHECK_GT(streams[i].width, 0); diff --git a/modules/video_coding/video_codec_initializer_unittest.cc b/modules/video_coding/video_codec_initializer_unittest.cc index 902f991f29..85efa467d6 100644 --- a/modules/video_coding/video_codec_initializer_unittest.cc +++ b/modules/video_coding/video_codec_initializer_unittest.cc @@ -430,7 +430,7 @@ TEST_F(VideoCodecInitializerTest, Av1SingleSpatialLayerBitratesAreConsistent) { VideoEncoderConfig config; config.codec_type = VideoCodecType::kVideoCodecAV1; std::vector streams = {DefaultStream()}; - streams[0].scalability_mode = "L1T2"; + streams[0].scalability_mode = ScalabilityMode::kL1T2; VideoCodec codec; EXPECT_TRUE(VideoCodecInitializer::SetupCodec(config, streams, &codec)); @@ -445,7 +445,7 @@ TEST_F(VideoCodecInitializerTest, Av1TwoSpatialLayersBitratesAreConsistent) { VideoEncoderConfig config; config.codec_type = VideoCodecType::kVideoCodecAV1; std::vector streams = {DefaultStream()}; - streams[0].scalability_mode = "L2T2"; + streams[0].scalability_mode = ScalabilityMode::kL2T2; VideoCodec codec; EXPECT_TRUE(VideoCodecInitializer::SetupCodec(config, streams, &codec)); @@ -465,7 +465,7 @@ TEST_F(VideoCodecInitializerTest, Av1TwoSpatialLayersActiveByDefault) { VideoEncoderConfig config; config.codec_type = VideoCodecType::kVideoCodecAV1; std::vector streams = {DefaultStream()}; - streams[0].scalability_mode = "L2T2"; + streams[0].scalability_mode = ScalabilityMode::kL2T2; config.spatial_layers = {}; VideoCodec codec; @@ -479,7 +479,7 @@ TEST_F(VideoCodecInitializerTest, Av1TwoSpatialLayersOneDeactivated) { VideoEncoderConfig config; config.codec_type = VideoCodecType::kVideoCodecAV1; std::vector streams = {DefaultStream()}; - streams[0].scalability_mode = "L2T2"; + streams[0].scalability_mode = ScalabilityMode::kL2T2; config.spatial_layers.resize(2); config.spatial_layers[0].active = true; config.spatial_layers[1].active = false; diff --git a/modules/video_coding/video_coding_impl.cc b/modules/video_coding/video_coding_impl.cc index 0129aa1bf6..3e105a6b01 100644 --- a/modules/video_coding/video_coding_impl.cc +++ b/modules/video_coding/video_coding_impl.cc @@ -13,10 +13,13 @@ #include #include +#include "api/field_trials_view.h" #include "api/sequence_checker.h" +#include "api/transport/field_trial_based_config.h" #include "api/video/encoded_image.h" #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/timing.h" +#include "rtc_base/memory/always_valid_pointer.h" #include "system_wrappers/include/clock.h" namespace webrtc { @@ -41,10 +44,12 @@ namespace { class VideoCodingModuleImpl : public VideoCodingModule { public: - explicit VideoCodingModuleImpl(Clock* clock) + explicit VideoCodingModuleImpl(Clock* clock, + const FieldTrialsView* field_trials) : VideoCodingModule(), - timing_(new VCMTiming(clock)), - receiver_(clock, timing_.get()) {} + field_trials_(field_trials), + timing_(new VCMTiming(clock, *field_trials_)), + receiver_(clock, timing_.get(), *field_trials_) {} ~VideoCodingModuleImpl() override {} @@ -104,6 +109,8 @@ class VideoCodingModuleImpl : public VideoCodingModule { } private: + AlwaysValidPointer + field_trials_; SequenceChecker construction_thread_; const std::unique_ptr timing_; vcm::VideoReceiver receiver_; @@ -112,9 +119,11 @@ class VideoCodingModuleImpl : public VideoCodingModule { // DEPRECATED. Create method for current interface, will be removed when the // new jitter buffer is in place. -VideoCodingModule* VideoCodingModule::Create(Clock* clock) { +VideoCodingModule* VideoCodingModule::Create( + Clock* clock, + const FieldTrialsView* field_trials) { RTC_DCHECK(clock); - return new VideoCodingModuleImpl(clock); + return new VideoCodingModuleImpl(clock, field_trials); } } // namespace webrtc diff --git a/modules/video_coding/video_coding_impl.h b/modules/video_coding/video_coding_impl.h index 10ebd41bc8..3010b2fa76 100644 --- a/modules/video_coding/video_coding_impl.h +++ b/modules/video_coding/video_coding_impl.h @@ -16,6 +16,7 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "modules/video_coding/decoder_database.h" #include "modules/video_coding/frame_buffer.h" @@ -56,7 +57,9 @@ class VCMProcessTimer { class VideoReceiver : public Module { public: - VideoReceiver(Clock* clock, VCMTiming* timing); + VideoReceiver(Clock* clock, + VCMTiming* timing, + const FieldTrialsView& field_trials); ~VideoReceiver() override; void RegisterReceiveCodec(uint8_t payload_type, diff --git a/modules/video_coding/video_receiver.cc b/modules/video_coding/video_receiver.cc index 079de6de14..6db27c4817 100644 --- a/modules/video_coding/video_receiver.cc +++ b/modules/video_coding/video_receiver.cc @@ -40,11 +40,13 @@ namespace webrtc { namespace vcm { -VideoReceiver::VideoReceiver(Clock* clock, VCMTiming* timing) +VideoReceiver::VideoReceiver(Clock* clock, + VCMTiming* timing, + const FieldTrialsView& field_trials) : clock_(clock), _timing(timing), - _receiver(_timing, clock_), - _decodedFrameCallback(_timing, clock_), + _receiver(_timing, clock_, field_trials), + _decodedFrameCallback(_timing, clock_, field_trials), _frameTypeCallback(nullptr), _packetRequestCallback(nullptr), _scheduleKeyRequest(false), @@ -203,8 +205,9 @@ int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs) { } // If this frame was too late, we should adjust the delay accordingly - _timing->UpdateCurrentDelay(frame->RenderTimeMs(), - clock_->TimeInMilliseconds()); + if (frame->RenderTimeMs() > 0) + _timing->UpdateCurrentDelay(Timestamp::Millis(frame->RenderTimeMs()), + clock_->CurrentTime()); if (first_frame_received_()) { RTC_LOG(LS_INFO) << "Received first complete decodable video frame"; diff --git a/modules/video_coding/video_receiver2.cc b/modules/video_coding/video_receiver2.cc index ef6dcf98c5..8557d6884c 100644 --- a/modules/video_coding/video_receiver2.cc +++ b/modules/video_coding/video_receiver2.cc @@ -28,10 +28,12 @@ namespace webrtc { -VideoReceiver2::VideoReceiver2(Clock* clock, VCMTiming* timing) +VideoReceiver2::VideoReceiver2(Clock* clock, + VCMTiming* timing, + const FieldTrialsView& field_trials) : clock_(clock), timing_(timing), - decodedFrameCallback_(timing_, clock_), + decodedFrameCallback_(timing_, clock_, field_trials), codecDataBase_() { decoder_sequence_checker_.Detach(); } diff --git a/modules/video_coding/video_receiver2.h b/modules/video_coding/video_receiver2.h index 5e087d333f..a634e0ef8f 100644 --- a/modules/video_coding/video_receiver2.h +++ b/modules/video_coding/video_receiver2.h @@ -11,6 +11,7 @@ #ifndef MODULES_VIDEO_CODING_VIDEO_RECEIVER2_H_ #define MODULES_VIDEO_CODING_VIDEO_RECEIVER2_H_ +#include "api/field_trials_view.h" #include "api/sequence_checker.h" #include "api/video_codecs/video_decoder.h" #include "modules/video_coding/decoder_database.h" @@ -28,7 +29,9 @@ namespace webrtc { // VideoCodingModule api. class VideoReceiver2 { public: - VideoReceiver2(Clock* clock, VCMTiming* timing); + VideoReceiver2(Clock* clock, + VCMTiming* timing, + const FieldTrialsView& field_trials); ~VideoReceiver2(); void RegisterReceiveCodec(uint8_t payload_type, diff --git a/modules/video_coding/video_receiver_unittest.cc b/modules/video_coding/video_receiver_unittest.cc index fc83141380..148ec0dfa9 100644 --- a/modules/video_coding/video_receiver_unittest.cc +++ b/modules/video_coding/video_receiver_unittest.cc @@ -15,6 +15,7 @@ #include "modules/video_coding/video_coding_impl.h" #include "system_wrappers/include/clock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::AnyNumber; @@ -51,7 +52,9 @@ class TestVideoReceiver : public ::testing::Test { static const uint16_t kMaxWaitTimeMs = 100; TestVideoReceiver() - : clock_(0), timing_(&clock_), receiver_(&clock_, &timing_) {} + : clock_(0), + timing_(&clock_, field_trials_), + receiver_(&clock_, &timing_, field_trials_) {} virtual void SetUp() { // Register decoder. @@ -118,6 +121,7 @@ class TestVideoReceiver : public ::testing::Test { EXPECT_EQ(0, receiver_.Decode(kMaxWaitTimeMs)); } + test::ScopedKeyValueConfig field_trials_; SimulatedClock clock_; NiceMock decoder_; NiceMock packet_request_callback_; diff --git a/modules/video_processing/BUILD.gn b/modules/video_processing/BUILD.gn index daffd49cdf..b3461186a2 100644 --- a/modules/video_processing/BUILD.gn +++ b/modules/video_processing/BUILD.gn @@ -33,7 +33,6 @@ rtc_library("video_processing") { "../../common_video", "../../modules/utility", "../../rtc_base:checks", - "../../rtc_base:rtc_base_approved", "../../rtc_base/system:arch", "../../system_wrappers", "//third_party/libyuv", @@ -61,7 +60,6 @@ if (build_video_processing_sse2) { deps = [ ":denoiser_filter", - "../../rtc_base:rtc_base_approved", "../../system_wrappers", ] diff --git a/net/dcsctp/common/BUILD.gn b/net/dcsctp/common/BUILD.gn index 273a002f8b..251ebaaf91 100644 --- a/net/dcsctp/common/BUILD.gn +++ b/net/dcsctp/common/BUILD.gn @@ -10,7 +10,7 @@ import("../../../webrtc.gni") rtc_source_set("internal_types") { deps = [ - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:strong_alias", "../public:types", ] sources = [ "internal_types.h" ] @@ -44,7 +44,6 @@ if (rtc_include_tests) { "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", ] sources = [ diff --git a/net/dcsctp/fuzzers/BUILD.gn b/net/dcsctp/fuzzers/BUILD.gn index c00a02251c..302c828684 100644 --- a/net/dcsctp/fuzzers/BUILD.gn +++ b/net/dcsctp/fuzzers/BUILD.gn @@ -13,9 +13,8 @@ rtc_library("dcsctp_fuzzers") { deps = [ "../../../api:array_view", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../common:math", "../packet:chunk", "../packet:error_cause", @@ -39,7 +38,7 @@ if (rtc_include_tests) { "../../../api:array_view", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../../../test:test_support", "../packet:sctp_packet", "../public:socket", diff --git a/net/dcsctp/packet/BUILD.gn b/net/dcsctp/packet/BUILD.gn index 9c08ebc80e..d9d0b05805 100644 --- a/net/dcsctp/packet/BUILD.gn +++ b/net/dcsctp/packet/BUILD.gn @@ -15,9 +15,7 @@ group("packet") { rtc_source_set("bounded_io") { deps = [ "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", ] sources = [ "bounded_byte_reader.h", @@ -29,9 +27,8 @@ rtc_library("tlv_trait") { deps = [ ":bounded_io", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings", @@ -45,9 +42,7 @@ rtc_library("tlv_trait") { rtc_source_set("data") { deps = [ - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../common:internal_types", "../public:types", ] @@ -57,9 +52,7 @@ rtc_source_set("data") { rtc_library("crc32c") { deps = [ "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "//third_party/crc32c", ] sources = [ @@ -74,9 +67,9 @@ rtc_library("parameter") { ":data", ":tlv_trait", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:internal_types", "../common:math", "../common:str_join", @@ -119,9 +112,9 @@ rtc_library("error_cause") { ":parameter", ":tlv_trait", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:internal_types", "../common:math", "../common:str_join", @@ -172,9 +165,9 @@ rtc_library("chunk") { ":parameter", ":tlv_trait", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:math", "../common:str_join", "../packet:bounded_io", @@ -229,9 +222,8 @@ rtc_library("chunk") { rtc_library("chunk_validators") { deps = [ ":chunk", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", ] sources = [ "chunk_validators.cc", @@ -245,9 +237,9 @@ rtc_library("sctp_packet") { ":chunk", ":crc32c", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:internal_types", "../common:math", "../public:types", @@ -276,9 +268,9 @@ if (rtc_include_tests) { ":sctp_packet", ":tlv_trait", "../../../api:array_view", + "../../../rtc_base:buffer", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "../common:internal_types", "../common:math", diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc index f01505094d..e432114c50 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk.cc @@ -87,6 +87,9 @@ void ForwardTsnChunk::SerializeTo(std::vector& out) const { std::string ForwardTsnChunk::ToString() const { rtc::StringBuilder sb; sb << "FORWARD-TSN, new_cumulative_tsn=" << *new_cumulative_tsn(); + for (const auto& skipped : skipped_streams()) { + sb << ", skip " << skipped.stream_id.value() << ":" << *skipped.ssn; + } return sb.str(); } } // namespace dcsctp diff --git a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc index 9420c1f2ef..51f97f2396 100644 --- a/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc +++ b/net/dcsctp/packet/chunk/forward_tsn_chunk_test.cc @@ -56,7 +56,8 @@ TEST(ForwardTsnChunkTest, SerializeAndDeserialize) { ElementsAre(ForwardTsnChunk::SkippedStream(StreamID(1), SSN(23)), ForwardTsnChunk::SkippedStream(StreamID(42), SSN(99)))); - EXPECT_EQ(deserialized.ToString(), "FORWARD-TSN, new_cumulative_tsn=123"); + EXPECT_EQ(deserialized.ToString(), + "FORWARD-TSN, new_cumulative_tsn=123, skip 1:23, skip 42:99"); } } // namespace diff --git a/net/dcsctp/public/BUILD.gn b/net/dcsctp/public/BUILD.gn index 4fad23e9b8..6cb289bf5b 100644 --- a/net/dcsctp/public/BUILD.gn +++ b/net/dcsctp/public/BUILD.gn @@ -11,7 +11,7 @@ import("../../../webrtc.gni") rtc_source_set("types") { deps = [ "../../../api:array_view", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:strong_alias", ] sources = [ "dcsctp_message.h", @@ -26,9 +26,8 @@ rtc_source_set("socket") { ":types", "../../../api:array_view", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:strong_alias", ] sources = [ "dcsctp_handover_state.cc", @@ -58,8 +57,12 @@ rtc_source_set("factory") { rtc_source_set("mocks") { testonly = true - sources = [ "mock_dcsctp_socket.h" ] + sources = [ + "mock_dcsctp_socket.h", + "mock_dcsctp_socket_factory.h", + ] deps = [ + ":factory", ":socket", "../../../test:test_support", ] @@ -90,7 +93,6 @@ if (rtc_include_tests) { ":types", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", ] sources = [ diff --git a/net/dcsctp/public/dcsctp_socket_factory.cc b/net/dcsctp/public/dcsctp_socket_factory.cc index 338d143424..ebcb5553e3 100644 --- a/net/dcsctp/public/dcsctp_socket_factory.cc +++ b/net/dcsctp/public/dcsctp_socket_factory.cc @@ -20,6 +20,9 @@ #include "net/dcsctp/socket/dcsctp_socket.h" namespace dcsctp { + +DcSctpSocketFactory::~DcSctpSocketFactory() = default; + std::unique_ptr DcSctpSocketFactory::Create( absl::string_view log_prefix, DcSctpSocketCallbacks& callbacks, diff --git a/net/dcsctp/public/dcsctp_socket_factory.h b/net/dcsctp/public/dcsctp_socket_factory.h index dcc68d9b54..ca429d3275 100644 --- a/net/dcsctp/public/dcsctp_socket_factory.h +++ b/net/dcsctp/public/dcsctp_socket_factory.h @@ -20,7 +20,8 @@ namespace dcsctp { class DcSctpSocketFactory { public: - std::unique_ptr Create( + virtual ~DcSctpSocketFactory(); + virtual std::unique_ptr Create( absl::string_view log_prefix, DcSctpSocketCallbacks& callbacks, std::unique_ptr packet_observer, diff --git a/net/dcsctp/public/mock_dcsctp_socket_factory.h b/net/dcsctp/public/mock_dcsctp_socket_factory.h new file mode 100644 index 0000000000..61f05577f2 --- /dev/null +++ b/net/dcsctp/public/mock_dcsctp_socket_factory.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 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 NET_DCSCTP_PUBLIC_MOCK_DCSCTP_SOCKET_FACTORY_H_ +#define NET_DCSCTP_PUBLIC_MOCK_DCSCTP_SOCKET_FACTORY_H_ + +#include + +#include "net/dcsctp/public/dcsctp_socket_factory.h" +#include "test/gmock.h" + +namespace dcsctp { + +class MockDcSctpSocketFactory : public DcSctpSocketFactory { + public: + MOCK_METHOD(std::unique_ptr, + Create, + (absl::string_view log_prefix, + DcSctpSocketCallbacks& callbacks, + std::unique_ptr packet_observer, + const DcSctpOptions& options), + (override)); +}; + +} // namespace dcsctp + +#endif // NET_DCSCTP_PUBLIC_MOCK_DCSCTP_SOCKET_FACTORY_H_ diff --git a/net/dcsctp/public/types.h b/net/dcsctp/public/types.h index 8faec08ad3..caa03bb96f 100644 --- a/net/dcsctp/public/types.h +++ b/net/dcsctp/public/types.h @@ -107,10 +107,11 @@ constexpr inline DurationMs operator-(TimeMs lhs, TimeMs rhs) { // The maximum number of times the socket should attempt to retransmit a // message which fails the first time in unreliable mode. -class MaxRetransmits : public webrtc::StrongAlias { +class MaxRetransmits + : public webrtc::StrongAlias { public: constexpr explicit MaxRetransmits(const UnderlyingType& v) - : webrtc::StrongAlias(v) {} + : webrtc::StrongAlias(v) {} // There should be no limit - the message should be sent reliably. static constexpr MaxRetransmits NoLimit() { diff --git a/net/dcsctp/rx/BUILD.gn b/net/dcsctp/rx/BUILD.gn index de96678a3e..d24f6d6fb3 100644 --- a/net/dcsctp/rx/BUILD.gn +++ b/net/dcsctp/rx/BUILD.gn @@ -11,9 +11,9 @@ import("../../../webrtc.gni") rtc_library("data_tracker") { deps = [ "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:sequence_numbers", "../packet:chunk", "../packet:data", @@ -48,9 +48,8 @@ rtc_library("traditional_reassembly_streams") { deps = [ ":reassembly_streams", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../common:sequence_numbers", "../packet:chunk", "../packet:data", @@ -72,9 +71,8 @@ rtc_library("reassembly_queue") { ":reassembly_streams", ":traditional_reassembly_streams", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../common:internal_types", "../common:sequence_numbers", "../common:str_join", @@ -107,7 +105,6 @@ if (rtc_include_tests) { "../../../api/task_queue:task_queue", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "../common:handover_testing", "../common:sequence_numbers", diff --git a/net/dcsctp/rx/data_tracker.cc b/net/dcsctp/rx/data_tracker.cc index f31847b524..8faee9e7d2 100644 --- a/net/dcsctp/rx/data_tracker.cc +++ b/net/dcsctp/rx/data_tracker.cc @@ -123,8 +123,9 @@ bool DataTracker::IsTSNValid(TSN tsn) const { return true; } -void DataTracker::Observe(TSN tsn, +bool DataTracker::Observe(TSN tsn, AnyDataChunk::ImmediateAckFlag immediate_ack) { + bool is_duplicate = false; UnwrappedTSN unwrapped_tsn = tsn_unwrapper_.Unwrap(tsn); // IsTSNValid must be called prior to calling this method. @@ -143,6 +144,7 @@ void DataTracker::Observe(TSN tsn, // packet arrives with duplicate DATA chunk(s) bundled with new DATA chunks, // the endpoint MAY immediately send a SACK." UpdateAckState(AckState::kImmediate, "duplicate data"); + is_duplicate = true; } else { if (unwrapped_tsn == last_cumulative_acked_tsn_.next_value()) { last_cumulative_acked_tsn_ = unwrapped_tsn; @@ -167,6 +169,7 @@ void DataTracker::Observe(TSN tsn, // delay. If a packet arrives with duplicate DATA chunk(s) bundled with // new DATA chunks, the endpoint MAY immediately send a SACK." // No need to do this. SACKs are sent immediately on packet loss below. + is_duplicate = true; } } } @@ -208,6 +211,7 @@ void DataTracker::Observe(TSN tsn, } else if (ack_state_ == AckState::kDelayed) { UpdateAckState(AckState::kImmediate, "received DATA when already delayed"); } + return !is_duplicate; } void DataTracker::HandleForwardTsn(TSN new_cumulative_ack) { diff --git a/net/dcsctp/rx/data_tracker.h b/net/dcsctp/rx/data_tracker.h index fb8add82a2..603a237245 100644 --- a/net/dcsctp/rx/data_tracker.h +++ b/net/dcsctp/rx/data_tracker.h @@ -69,8 +69,9 @@ class DataTracker { // means that there is intentional packet loss. bool IsTSNValid(TSN tsn) const; - // Call for every incoming data chunk. - void Observe(TSN tsn, + // Call for every incoming data chunk. Returns `true` if `tsn` was seen for + // the first time, and `false` if it has been seen before (a duplicate `tsn`). + bool Observe(TSN tsn, AnyDataChunk::ImmediateAckFlag immediate_ack = AnyDataChunk::ImmediateAckFlag(false)); // Called at the end of processing an SCTP packet. diff --git a/net/dcsctp/rx/data_tracker_test.cc b/net/dcsctp/rx/data_tracker_test.cc index 3d8380a05c..43494734b6 100644 --- a/net/dcsctp/rx/data_tracker_test.cc +++ b/net/dcsctp/rx/data_tracker_test.cc @@ -48,9 +48,16 @@ class DataTrackerTest : public testing::Test { std::make_unique("log: ", timer_.get(), kInitialTSN)) { } - void Observer(std::initializer_list tsns) { + void Observer(std::initializer_list tsns, + bool expect_as_duplicate = false) { for (const uint32_t tsn : tsns) { - tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false)); + if (expect_as_duplicate) { + EXPECT_FALSE( + tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false))); + } else { + EXPECT_TRUE( + tracker_->Observe(TSN(tsn), AnyDataChunk::ImmediateAckFlag(false))); + } } } @@ -125,7 +132,7 @@ TEST_F(DataTrackerTest, AckAlreadyReceivedChunk) { EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty()); // Receive old chunk - Observer({8}); + Observer({8}, /*expect_as_duplicate=*/true); SackChunk sack2 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack2.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty()); @@ -145,7 +152,8 @@ TEST_F(DataTrackerTest, DoubleSendRetransmittedChunk) { EXPECT_THAT(sack2.gap_ack_blocks(), IsEmpty()); // Receive chunk 12 again. - Observer({12, 19, 20, 21}); + Observer({12}, /*expect_as_duplicate=*/true); + Observer({19, 20, 21}); SackChunk sack3 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack3.cumulative_tsn_ack(), TSN(21)); EXPECT_THAT(sack3.gap_ack_blocks(), IsEmpty()); @@ -176,7 +184,8 @@ TEST_F(DataTrackerTest, ForwardTsnSkipsFromGapBlock) { TEST_F(DataTrackerTest, ExampleFromRFC3758) { tracker_->HandleForwardTsn(TSN(102)); - Observer({102, 104, 105, 107}); + Observer({102}, /*expect_as_duplicate=*/true); + Observer({104, 105, 107}); tracker_->HandleForwardTsn(TSN(103)); @@ -246,7 +255,8 @@ TEST_F(DataTrackerTest, WillNotAcceptInvalidTSNs) { } TEST_F(DataTrackerTest, ReportSingleDuplicateTsns) { - Observer({11, 12, 11}); + Observer({11, 12}); + Observer({11}, /*expect_as_duplicate=*/true); SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(12)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); @@ -254,7 +264,9 @@ TEST_F(DataTrackerTest, ReportSingleDuplicateTsns) { } TEST_F(DataTrackerTest, ReportMultipleDuplicateTsns) { - Observer({11, 12, 13, 14, 12, 13, 12, 13, 15, 16}); + Observer({11, 12, 13, 14}); + Observer({12, 13, 12, 13}, /*expect_as_duplicate=*/true); + Observer({15, 16}); SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(16)); EXPECT_THAT(sack.gap_ack_blocks(), IsEmpty()); @@ -262,7 +274,9 @@ TEST_F(DataTrackerTest, ReportMultipleDuplicateTsns) { } TEST_F(DataTrackerTest, ReportDuplicateTsnsInGapAckBlocks) { - Observer({11, /*12,*/ 13, 14, 13, 14, 15, 16}); + Observer({11, /*12,*/ 13, 14}); + Observer({13, 14}, /*expect_as_duplicate=*/true); + Observer({15, 16}); SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(11)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 5))); @@ -270,7 +284,9 @@ TEST_F(DataTrackerTest, ReportDuplicateTsnsInGapAckBlocks) { } TEST_F(DataTrackerTest, ClearsDuplicateTsnsAfterCreatingSack) { - Observer({11, 12, 13, 14, 12, 13, 12, 13, 15, 16}); + Observer({11, 12, 13, 14}); + Observer({12, 13, 12, 13}, /*expect_as_duplicate=*/true); + Observer({15, 16}); SackChunk sack1 = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack1.cumulative_tsn_ack(), TSN(16)); EXPECT_THAT(sack1.gap_ack_blocks(), IsEmpty()); @@ -380,7 +396,7 @@ TEST_F(DataTrackerTest, SendsSackOnDuplicateDataChunks) { tracker_->ObservePacketEnd(); EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); - Observer({11}); + Observer({11}, /*expect_as_duplicate=*/true); tracker_->ObservePacketEnd(); EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); @@ -394,7 +410,7 @@ TEST_F(DataTrackerTest, SendsSackOnDuplicateDataChunks) { EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); // Duplicate again - Observer({12}); + Observer({12}, /*expect_as_duplicate=*/true); tracker_->ObservePacketEnd(); EXPECT_TRUE(tracker_->ShouldSendAck()); EXPECT_FALSE(timer_->is_running()); @@ -418,7 +434,7 @@ TEST_F(DataTrackerTest, GapAckBlockAddsAnother) { TEST_F(DataTrackerTest, GapAckBlockAddsDuplicate) { Observer({12}); - Observer({12}); + Observer({12}, /*expect_as_duplicate=*/true); SackChunk sack = tracker_->CreateSelectiveAck(kArwnd); EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(10)); EXPECT_THAT(sack.gap_ack_blocks(), ElementsAre(SackChunk::GapAckBlock(2, 2))); diff --git a/net/dcsctp/rx/reassembly_queue.cc b/net/dcsctp/rx/reassembly_queue.cc index 5791d6805c..cbf198b136 100644 --- a/net/dcsctp/rx/reassembly_queue.cc +++ b/net/dcsctp/rx/reassembly_queue.cc @@ -210,8 +210,16 @@ void ReassemblyQueue::AddReassembledMessage( << ", payload=" << message.payload().size() << " bytes"; for (const UnwrappedTSN tsn : tsns) { - // Update watermark, or insert into delivered_tsns_ - if (tsn == last_assembled_tsn_watermark_.next_value()) { + if (tsn <= last_assembled_tsn_watermark_) { + // This can be provoked by a misbehaving peer by sending FORWARD-TSN with + // invalid SSNs, allowing ordered messages to stay in the queue that + // should've been discarded. + RTC_DLOG(LS_VERBOSE) + << log_prefix_ + << "Message is built from fragments already seen - skipping"; + return; + } else if (tsn == last_assembled_tsn_watermark_.next_value()) { + // Update watermark, or insert into delivered_tsns_ last_assembled_tsn_watermark_.Increment(); } else { delivered_tsns_.insert(tsn); diff --git a/net/dcsctp/rx/reassembly_queue_test.cc b/net/dcsctp/rx/reassembly_queue_test.cc index d1e3bf6413..bc1b776837 100644 --- a/net/dcsctp/rx/reassembly_queue_test.cc +++ b/net/dcsctp/rx/reassembly_queue_test.cc @@ -389,5 +389,21 @@ TEST_F(ReassemblyQueueTest, HandoverAfterHavingAssembedOneMessage) { reasm2.Add(TSN(11), gen_.Ordered({1, 2, 3, 4}, "BE")); EXPECT_THAT(reasm2.FlushMessages(), SizeIs(1)); } + +TEST_F(ReassemblyQueueTest, HandleInconsistentForwardTSN) { + // Found when fuzzing. + ReassemblyQueue reasm("log: ", TSN(10), kBufferSize); + // Add TSN=43, SSN=7. Can't be reassembled as previous SSNs aren't known. + reasm.Add(TSN(43), Data(kStreamID, SSN(7), MID(0), FSN(0), kPPID, + std::vector(10), Data::IsBeginning(true), + Data::IsEnd(true), IsUnordered(false))); + + // Invalid, as TSN=44 have to have SSN>=7, but peer says 6. + reasm.Handle(ForwardTsnChunk( + TSN(44), {ForwardTsnChunk::SkippedStream(kStreamID, SSN(6))})); + + // Don't assemble SSN=7, as that TSN is skipped. + EXPECT_FALSE(reasm.HasMessages()); +} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/rx/traditional_reassembly_streams.cc b/net/dcsctp/rx/traditional_reassembly_streams.cc index 4ccc2e5278..f5dc8cacc8 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams.cc +++ b/net/dcsctp/rx/traditional_reassembly_streams.cc @@ -261,11 +261,11 @@ size_t TraditionalReassemblyStreams::OrderedStream::EraseTo(SSN ssn) { int TraditionalReassemblyStreams::Add(UnwrappedTSN tsn, Data data) { if (data.is_unordered) { - auto it = unordered_streams_.emplace(data.stream_id, this).first; + auto it = unordered_streams_.try_emplace(data.stream_id, this).first; return it->second.Add(tsn, std::move(data)); } - auto it = ordered_streams_.emplace(data.stream_id, this).first; + auto it = ordered_streams_.try_emplace(data.stream_id, this).first; return it->second.Add(tsn, std::move(data)); } @@ -280,10 +280,9 @@ size_t TraditionalReassemblyStreams::HandleForwardTsn( } for (const auto& skipped_stream : skipped_streams) { - auto it = ordered_streams_.find(skipped_stream.stream_id); - if (it != ordered_streams_.end()) { - bytes_removed += it->second.EraseTo(skipped_stream.ssn); - } + auto it = + ordered_streams_.try_emplace(skipped_stream.stream_id, this).first; + bytes_removed += it->second.EraseTo(skipped_stream.ssn); } return bytes_removed; diff --git a/net/dcsctp/rx/traditional_reassembly_streams_test.cc b/net/dcsctp/rx/traditional_reassembly_streams_test.cc index 3e6f560aba..759962473d 100644 --- a/net/dcsctp/rx/traditional_reassembly_streams_test.cc +++ b/net/dcsctp/rx/traditional_reassembly_streams_test.cc @@ -25,8 +25,10 @@ namespace dcsctp { namespace { +using ::testing::ElementsAre; using ::testing::MockFunction; using ::testing::NiceMock; +using ::testing::Property; class TraditionalReassemblyStreamsTest : public testing::Test { protected: @@ -232,5 +234,24 @@ TEST_F(TraditionalReassemblyStreamsTest, EXPECT_EQ(streams2.Add(tsn(4), gen_.Unordered({7})), 1); } +TEST_F(TraditionalReassemblyStreamsTest, CanDeleteFirstOrderedMessage) { + NiceMock> on_assembled; + EXPECT_CALL(on_assembled, + Call(ElementsAre(tsn(2)), + Property(&DcSctpMessage::payload, ElementsAre(2, 3, 4)))); + + TraditionalReassemblyStreams streams("", on_assembled.AsStdFunction()); + + // Not received, SID=1. TSN=1, SSN=0 + gen_.Ordered({1}, "BE"); + // And deleted (SID=1, TSN=1, SSN=0) + ForwardTsnChunk::SkippedStream skipped[] = { + ForwardTsnChunk::SkippedStream(StreamID(1), SSN(0))}; + EXPECT_EQ(streams.HandleForwardTsn(tsn(1), skipped), 0u); + + // Receive SID=1, TSN=2, SSN=1 + EXPECT_EQ(streams.Add(tsn(2), gen_.Ordered({2, 3, 4}, "BE")), 0); +} + } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/BUILD.gn b/net/dcsctp/socket/BUILD.gn index 083adaa9dd..7d5c5f22aa 100644 --- a/net/dcsctp/socket/BUILD.gn +++ b/net/dcsctp/socket/BUILD.gn @@ -23,9 +23,8 @@ rtc_library("heartbeat_handler") { deps = [ ":context", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../packet:bounded_io", "../packet:chunk", "../packet:parameter", @@ -49,9 +48,8 @@ rtc_library("stream_reset_handler") { deps = [ ":context", "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../../../rtc_base/containers:flat_set", "../common:internal_types", "../common:str_join", @@ -99,9 +97,9 @@ rtc_library("transmission_control_block") { ":stream_reset_handler", "../../../api:array_view", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:sequence_numbers", "../packet:chunk", "../packet:sctp_packet", @@ -137,10 +135,12 @@ rtc_library("dcsctp_socket") { "../../../api:array_view", "../../../api:refcountedbase", "../../../api:scoped_refptr", + "../../../api:sequence_checker", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:refcount", + "../../../rtc_base:stringutils", "../common:internal_types", "../packet:bounded_io", "../packet:chunk", @@ -184,7 +184,7 @@ if (rtc_include_tests) { "../../../api:array_view", "../../../api/task_queue:task_queue", "../../../rtc_base:logging", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:random", "../../../test:test_support", "../public:socket", "../public:types", @@ -231,11 +231,13 @@ if (rtc_include_tests) { "../../../api/units:time_delta", "../../../call:simulated_network", "../../../rtc_base:checks", + "../../../rtc_base:copy_on_write_buffer", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../../../rtc_base:rtc_base_tests_utils", "../../../rtc_base:socket_address", + "../../../rtc_base:stringutils", + "../../../rtc_base:timeutils", "../../../rtc_base/task_utils:to_queued_task", "../../../test:test_support", "../common:handover_testing", diff --git a/net/dcsctp/socket/dcsctp_socket.cc b/net/dcsctp/socket/dcsctp_socket.cc index b93584ed47..d081bd95b8 100644 --- a/net/dcsctp/socket/dcsctp_socket.cc +++ b/net/dcsctp/socket/dcsctp_socket.cc @@ -479,13 +479,7 @@ ResetStreamsStatus DcSctpSocket::ResetStreams( } tcb_->stream_reset_handler().ResetStreams(outgoing_streams); - absl::optional reconfig = - tcb_->stream_reset_handler().MakeStreamResetRequest(); - if (reconfig.has_value()) { - SctpPacket::Builder builder = tcb_->PacketBuilder(); - builder.Add(*reconfig); - packet_sender_.Send(builder); - } + MaybeSendResetStreamsRequest(); RTC_DCHECK(IsConsistent()); return ResetStreamsStatus::kPerformed; @@ -570,6 +564,16 @@ void DcSctpSocket::MaybeSendShutdownOnPacketReceived(const SctpPacket& packet) { } } +void DcSctpSocket::MaybeSendResetStreamsRequest() { + absl::optional reconfig = + tcb_->stream_reset_handler().MakeStreamResetRequest(); + if (reconfig.has_value()) { + SctpPacket::Builder builder = tcb_->PacketBuilder(); + builder.Add(*reconfig); + packet_sender_.Send(builder); + } +} + bool DcSctpSocket::ValidatePacket(const SctpPacket& packet) { const CommonHeader& header = packet.common_header(); VerificationTag my_verification_tag = @@ -798,7 +802,7 @@ bool DcSctpSocket::Dispatch(const CommonHeader& header, HandleIData(header, descriptor); break; case IForwardTsnChunk::kType: - HandleForwardTsn(header, descriptor); + HandleIForwardTsn(header, descriptor); break; default: return HandleUnrecognizedChunk(descriptor); @@ -1024,11 +1028,12 @@ void DcSctpSocket::HandleDataCommon(AnyDataChunk& chunk) { return; } - tcb_->data_tracker().Observe(tsn, immediate_ack); - tcb_->reassembly_queue().MaybeResetStreamsDeferred( - tcb_->data_tracker().last_cumulative_acked_tsn()); - tcb_->reassembly_queue().Add(tsn, std::move(data)); - DeliverReassembledMessages(); + if (tcb_->data_tracker().Observe(tsn, immediate_ack)) { + tcb_->reassembly_queue().MaybeResetStreamsDeferred( + tcb_->data_tracker().last_cumulative_acked_tsn()); + tcb_->reassembly_queue().Add(tsn, std::move(data)); + DeliverReassembledMessages(); + } } void DcSctpSocket::HandleInit(const CommonHeader& header, @@ -1386,6 +1391,17 @@ void DcSctpSocket::HandleSack(const CommonHeader& header, if (tcb_->retransmission_queue().HandleSack(now, sack)) { MaybeSendShutdownOrAck(); + // Receiving an ACK may make the socket go into fast recovery mode. + // https://datatracker.ietf.org/doc/html/rfc4960#section-7.2.4 + // "Determine how many of the earliest (i.e., lowest TSN) DATA chunks + // marked for retransmission will fit into a single packet, subject to + // constraint of the path MTU of the destination transport address to + // which the packet is being sent. Call this value K. Retransmit those K + // DATA chunks in a single packet. When a Fast Retransmit is being + // performed, the sender SHOULD ignore the value of cwnd and SHOULD NOT + // delay retransmission for this single packet." + tcb_->MaybeSendFastRetransmit(); + // Receiving an ACK will decrease outstanding bytes (maybe now below // cwnd?) or indicate packet loss that may result in sending FORWARD-TSN. tcb_->SendBufferedPackets(now); @@ -1462,6 +1478,10 @@ void DcSctpSocket::HandleReconfig( absl::optional chunk = ReConfigChunk::Parse(descriptor.data); if (ValidateParseSuccess(chunk) && ValidateHasTCB()) { tcb_->stream_reset_handler().HandleReConfig(*std::move(chunk)); + // Handling this response may result in outgoing stream resets finishing + // (either successfully or with failure). If there still are pending streams + // that were waiting for this request to finish, continue resetting them. + MaybeSendResetStreamsRequest(); } } diff --git a/net/dcsctp/socket/dcsctp_socket.h b/net/dcsctp/socket/dcsctp_socket.h index b1b3ea9d9b..0ab54e801a 100644 --- a/net/dcsctp/socket/dcsctp_socket.h +++ b/net/dcsctp/socket/dcsctp_socket.h @@ -155,6 +155,8 @@ class DcSctpSocket : public DcSctpSocketInterface { void MaybeSendShutdownOrAck(); // If the socket is shutting down, responds SHUTDOWN to any incoming DATA. void MaybeSendShutdownOnPacketReceived(const SctpPacket& packet); + // If there are streams pending to be reset, send a request to reset them. + void MaybeSendResetStreamsRequest(); // Sends a INIT chunk. void SendInit(); // Sends a SHUTDOWN chunk. diff --git a/net/dcsctp/socket/dcsctp_socket_test.cc b/net/dcsctp/socket/dcsctp_socket_test.cc index 66876e4e25..914bea34c4 100644 --- a/net/dcsctp/socket/dcsctp_socket_test.cc +++ b/net/dcsctp/socket/dcsctp_socket_test.cc @@ -27,7 +27,6 @@ #include "net/dcsctp/packet/chunk/data_chunk.h" #include "net/dcsctp/packet/chunk/data_common.h" #include "net/dcsctp/packet/chunk/error_chunk.h" -#include "net/dcsctp/packet/chunk/forward_tsn_chunk.h" #include "net/dcsctp/packet/chunk/heartbeat_ack_chunk.h" #include "net/dcsctp/packet/chunk/heartbeat_request_chunk.h" #include "net/dcsctp/packet/chunk/idata_chunk.h" @@ -37,6 +36,7 @@ #include "net/dcsctp/packet/error_cause/error_cause.h" #include "net/dcsctp/packet/error_cause/unrecognized_chunk_type_cause.h" #include "net/dcsctp/packet/parameter/heartbeat_info_parameter.h" +#include "net/dcsctp/packet/parameter/outgoing_ssn_reset_request_parameter.h" #include "net/dcsctp/packet/parameter/parameter.h" #include "net/dcsctp/packet/sctp_packet.h" #include "net/dcsctp/packet/tlv_trait.h" @@ -61,6 +61,7 @@ using ::testing::ElementsAre; using ::testing::HasSubstr; using ::testing::IsEmpty; using ::testing::SizeIs; +using ::testing::UnorderedElementsAre; constexpr SendOptions kSendOptions; constexpr size_t kLargeMessageSize = DcSctpOptions::kMaxSafeMTUSize * 20; @@ -229,17 +230,54 @@ MATCHER(HasSackWithNoGapAckBlocks, "") { return true; } +MATCHER_P(HasReconfigWithStreams, streams_matcher, "") { + absl::optional packet = SctpPacket::Parse(arg); + if (!packet.has_value()) { + *result_listener << "data didn't parse as an SctpPacket"; + return false; + } + + if (packet->descriptors()[0].type != ReConfigChunk::kType) { + *result_listener << "the first chunk in the packet is not a data chunk"; + return false; + } + + absl::optional reconfig = + ReConfigChunk::Parse(packet->descriptors()[0].data); + if (!reconfig.has_value()) { + *result_listener << "The first chunk didn't parse as a data chunk"; + return false; + } + + const Parameters& parameters = reconfig->parameters(); + if (parameters.descriptors().size() != 1 || + parameters.descriptors()[0].type != + OutgoingSSNResetRequestParameter::kType) { + *result_listener << "Expected the reconfig chunk to have an outgoing SSN " + "reset request parameter"; + return false; + } + + absl::optional p = + OutgoingSSNResetRequestParameter::Parse(parameters.descriptors()[0].data); + testing::Matcher> matcher = streams_matcher; + if (!matcher.MatchAndExplain(p->stream_ids(), result_listener)) { + return false; + } + + return true; +} + TSN AddTo(TSN tsn, int delta) { return TSN(*tsn + delta); } -DcSctpOptions MakeOptionsForTest(bool enable_message_interleaving) { - DcSctpOptions options; +DcSctpOptions FixupOptions(DcSctpOptions options = {}) { + DcSctpOptions fixup = options; // To make the interval more predictable in tests. - options.heartbeat_interval_include_rtt = false; - options.enable_message_interleaving = enable_message_interleaving; - options.max_burst = kMaxBurstPackets; - return options; + fixup.heartbeat_interval_include_rtt = false; + fixup.max_burst = kMaxBurstPackets; + return fixup; } std::unique_ptr GetPacketObserver(absl::string_view name) { @@ -249,102 +287,89 @@ std::unique_ptr GetPacketObserver(absl::string_view name) { return nullptr; } -class DcSctpSocketTest : public testing::Test { - protected: - explicit DcSctpSocketTest(bool enable_message_interleaving = false) - : options_(MakeOptionsForTest(enable_message_interleaving)), - cb_a_("A"), - cb_z_("Z"), - sock_a_(std::make_unique("A", - cb_a_, - GetPacketObserver("A"), - options_)), - sock_z_(std::make_unique("Z", - cb_z_, - GetPacketObserver("Z"), - options_)) {} - - void AdvanceTime(DurationMs duration) { - cb_a_.AdvanceTime(duration); - cb_z_.AdvanceTime(duration); - } +struct SocketUnderTest { + explicit SocketUnderTest(absl::string_view name, + const DcSctpOptions& opts = {}) + : options(FixupOptions(opts)), + cb(name), + socket(name, cb, GetPacketObserver(name), options) {} - static void ExchangeMessages(DcSctpSocket& sock_a, - MockDcSctpSocketCallbacks& cb_a, - DcSctpSocket& sock_z, - MockDcSctpSocketCallbacks& cb_z) { - bool delivered_packet = false; - do { - delivered_packet = false; - std::vector packet_from_a = cb_a.ConsumeSentPacket(); - if (!packet_from_a.empty()) { - delivered_packet = true; - sock_z.ReceivePacket(std::move(packet_from_a)); - } - std::vector packet_from_z = cb_z.ConsumeSentPacket(); - if (!packet_from_z.empty()) { - delivered_packet = true; - sock_a.ReceivePacket(std::move(packet_from_z)); - } - } while (delivered_packet); - } + const DcSctpOptions options; + testing::NiceMock cb; + DcSctpSocket socket; +}; - void RunTimers(MockDcSctpSocketCallbacks& cb, DcSctpSocket& socket) { - for (;;) { - absl::optional timeout_id = cb.GetNextExpiredTimeout(); - if (!timeout_id.has_value()) { - break; - } - socket.HandleTimeout(*timeout_id); +void ExchangeMessages(SocketUnderTest& a, SocketUnderTest& z) { + bool delivered_packet = false; + do { + delivered_packet = false; + std::vector packet_from_a = a.cb.ConsumeSentPacket(); + if (!packet_from_a.empty()) { + delivered_packet = true; + z.socket.ReceivePacket(std::move(packet_from_a)); } - } + std::vector packet_from_z = z.cb.ConsumeSentPacket(); + if (!packet_from_z.empty()) { + delivered_packet = true; + a.socket.ReceivePacket(std::move(packet_from_z)); + } + } while (delivered_packet); +} - void RunTimers() { - RunTimers(cb_a_, *sock_a_); - RunTimers(cb_z_, *sock_z_); +void RunTimers(SocketUnderTest& s) { + for (;;) { + absl::optional timeout_id = s.cb.GetNextExpiredTimeout(); + if (!timeout_id.has_value()) { + break; + } + s.socket.HandleTimeout(*timeout_id); } +} - // Calls Connect() on `sock_a_` and make the connection established. - void ConnectSockets() { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); +void AdvanceTime(SocketUnderTest& a, SocketUnderTest& z, DurationMs duration) { + a.cb.AdvanceTime(duration); + z.cb.AdvanceTime(duration); - sock_a_->Connect(); - // Z reads INIT, INIT_ACK, COOKIE_ECHO, COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + RunTimers(a); + RunTimers(z); +} - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); - } +// Calls Connect() on `sock_a_` and make the connection established. +void ConnectSockets(SocketUnderTest& a, SocketUnderTest& z) { + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); - void HandoverSocketZ() { - ASSERT_EQ(sock_z_->GetHandoverReadiness(), HandoverReadinessStatus()); - bool is_closed = sock_z_->state() == SocketState::kClosed; - if (!is_closed) { - EXPECT_CALL(cb_z_, OnClosed).Times(1); - } - absl::optional handover_state = - sock_z_->GetHandoverStateAndClose(); - EXPECT_TRUE(handover_state.has_value()); - g_handover_state_transformer_for_test(&*handover_state); - cb_z_.Reset(); - sock_z_ = std::make_unique("Z", cb_z_, GetPacketObserver("Z"), - options_); - if (!is_closed) { - EXPECT_CALL(cb_z_, OnConnected).Times(1); - } - sock_z_->RestoreFromState(*handover_state); - } + a.socket.Connect(); + // Z reads INIT, INIT_ACK, COOKIE_ECHO, COOKIE_ACK + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - const DcSctpOptions options_; - testing::NiceMock cb_a_; - testing::NiceMock cb_z_; - std::unique_ptr sock_a_; - std::unique_ptr sock_z_; -}; + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); +} + +std::unique_ptr HandoverSocket( + std::unique_ptr sut) { + EXPECT_EQ(sut->socket.GetHandoverReadiness(), HandoverReadinessStatus()); + + bool is_closed = sut->socket.state() == SocketState::kClosed; + if (!is_closed) { + EXPECT_CALL(sut->cb, OnClosed).Times(1); + } + absl::optional handover_state = + sut->socket.GetHandoverStateAndClose(); + EXPECT_TRUE(handover_state.has_value()); + g_handover_state_transformer_for_test(&*handover_state); + + auto handover_socket = std::make_unique("H", sut->options); + if (!is_closed) { + EXPECT_CALL(handover_socket->cb, OnConnected).Times(1); + } + handover_socket->socket.RestoreFromState(*handover_state); + return handover_socket; +} // Test parameter that controls whether to perform handovers during the test. A // test can have multiple points where it conditionally hands over socket Z. @@ -355,27 +380,31 @@ enum class HandoverMode { }; class DcSctpSocketParametrizedTest - : public DcSctpSocketTest, + : public ::testing::Test, public ::testing::WithParamInterface { protected: - // Trigger handover for socket Z depending on the current test param. - void MaybeHandoverSocketZ() { + // Trigger handover for `sut` depending on the current test param. + std::unique_ptr MaybeHandoverSocket( + std::unique_ptr sut) { if (GetParam() == HandoverMode::kPerformHandovers) { - HandoverSocketZ(); + return HandoverSocket(std::move(sut)); } + return sut; } + // Trigger handover for socket Z depending on the current test param. // Then checks message passing to verify the handed over socket is functional. - void MaybeHandoverSocketZAndSendMessage() { + void MaybeHandoverSocketAndSendMessage(SocketUnderTest& a, + std::unique_ptr z) { if (GetParam() == HandoverMode::kPerformHandovers) { - HandoverSocketZ(); + z = HandoverSocket(std::move(z)); } - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + ExchangeMessages(a, *z); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); } @@ -392,451 +421,492 @@ INSTANTIATE_TEST_SUITE_P(Handovers, : "NoHandover"; }); -TEST_F(DcSctpSocketTest, EstablishConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); +TEST(DcSctpSocketTest, EstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Connect(); + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, EstablishConnectionWithSetupCollision) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); - sock_a_->Connect(); - sock_z_->Connect(); +TEST(DcSctpSocketTest, EstablishConnectionWithSetupCollision) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + a.socket.Connect(); + z.socket.Connect(); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, z); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ShuttingDownWhileEstablishingConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(0); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - sock_a_->Connect(); +TEST(DcSctpSocketTest, ShuttingDownWhileEstablishingConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(0); + EXPECT_CALL(z.cb, OnConnected).Times(1); + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Drop COOKIE_ACK, just to more easily verify shutdown protocol. - cb_z_.ConsumeSentPacket(); + z.cb.ConsumeSentPacket(); // As Socket A has received INIT_ACK, it has a TCB and is connected, while // Socket Z needs to receive COOKIE_ECHO to get there. Socket A still has // timers running at this point. - EXPECT_EQ(sock_a_->state(), SocketState::kConnecting); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnecting); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); // Socket A is now shut down, which should make it stop those timers. - sock_a_->Shutdown(); + a.socket.Shutdown(); - EXPECT_CALL(cb_a_, OnClosed).Times(1); - EXPECT_CALL(cb_z_, OnClosed).Times(1); + EXPECT_CALL(a.cb, OnClosed).Times(1); + EXPECT_CALL(z.cb, OnClosed).Times(1); // Z reads SHUTDOWN, produces SHUTDOWN_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads SHUTDOWN_ACK, produces SHUTDOWN_COMPLETE - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads SHUTDOWN_COMPLETE. - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); - EXPECT_TRUE(cb_z_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); + EXPECT_TRUE(z.cb.ConsumeSentPacket().empty()); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); - EXPECT_EQ(sock_z_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); + EXPECT_EQ(z.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, EstablishSimultaneousConnection) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); - sock_a_->Connect(); +TEST(DcSctpSocketTest, EstablishSimultaneousConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); + a.socket.Connect(); // INIT isn't received by Z, as it wasn't ready yet. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); - sock_z_->Connect(); + z.socket.Connect(); // A reads INIT, produces INIT_ACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads INIT_ACK, sends COOKIE_ECHO - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ECHO - establishes connection. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); // Proceed with the remaining packets. - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, z); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, EstablishConnectionLostCookieAck) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(0); - EXPECT_CALL(cb_z_, OnConnectionRestarted).Times(0); +TEST(DcSctpSocketTest, EstablishConnectionLostCookieAck) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(0); + EXPECT_CALL(z.cb, OnConnectionRestarted).Times(0); - sock_a_->Connect(); + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // COOKIE_ACK is lost. - cb_z_.ConsumeSentPacket(); + z.cb.ConsumeSentPacket(); - EXPECT_EQ(sock_a_->state(), SocketState::kConnecting); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnecting); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); // This will make A re-send the COOKIE_ECHO - AdvanceTime(DurationMs(options_.t1_cookie_timeout)); - RunTimers(); + AdvanceTime(a, z, DurationMs(a.options.t1_cookie_timeout)); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendInitAndEstablishConnection) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendInitAndEstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // INIT is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, InitChunk::kType); - AdvanceTime(options_.t1_init_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.t1_init_timeout); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendingInitTooManyTimesAborts) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendingInitTooManyTimesAborts) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // INIT is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, InitChunk::kType); - for (int i = 0; i < *options_.max_init_retransmits; ++i) { - AdvanceTime(options_.t1_init_timeout * (1 << i)); - RunTimers(); + for (int i = 0; i < *a.options.max_init_retransmits; ++i) { + AdvanceTime(a, z, a.options.t1_init_timeout * (1 << i)); // INIT is resent ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket resent_init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(resent_init_packet.descriptors()[0].type, InitChunk::kType); } // Another timeout, after the max init retransmits. - AdvanceTime(options_.t1_init_timeout * (1 << *options_.max_init_retransmits)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime( + a, z, a.options.t1_init_timeout * (1 << *a.options.max_init_retransmits)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, ResendCookieEchoAndEstablishConnection) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendCookieEchoAndEstablishConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, CookieEchoChunk::kType); - AdvanceTime(options_.t1_init_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.t1_init_timeout); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); } -TEST_F(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, ResendingCookieEchoTooManyTimesAborts) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(init_packet.descriptors()[0].type, CookieEchoChunk::kType); - for (int i = 0; i < *options_.max_init_retransmits; ++i) { - AdvanceTime(options_.t1_cookie_timeout * (1 << i)); - RunTimers(); + for (int i = 0; i < *a.options.max_init_retransmits; ++i) { + AdvanceTime(a, z, a.options.t1_cookie_timeout * (1 << i)); // COOKIE_ECHO is resent ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket resent_init_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(resent_init_packet.descriptors()[0].type, CookieEchoChunk::kType); } // Another timeout, after the max init retransmits. - AdvanceTime(options_.t1_cookie_timeout * - (1 << *options_.max_init_retransmits)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime( + a, z, + a.options.t1_cookie_timeout * (1 << *a.options.max_init_retransmits)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), +TEST(DcSctpSocketTest, DoesntSendMorePacketsUntilCookieAckHasBeenReceived) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions); - sock_a_->Connect(); + a.socket.Connect(); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // COOKIE_ECHO is never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket cookie_echo_packet1, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_THAT(cookie_echo_packet1.descriptors(), SizeIs(2)); EXPECT_EQ(cookie_echo_packet1.descriptors()[0].type, CookieEchoChunk::kType); EXPECT_EQ(cookie_echo_packet1.descriptors()[1].type, DataChunk::kType); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // There are DATA chunks in the sent packet (that was lost), which means that // the T3-RTX timer is running, but as the socket is in kCookieEcho state, it // will be T1-COOKIE that drives retransmissions, so when the T3-RTX expires, // nothing should be retransmitted. - ASSERT_TRUE(options_.rto_initial < options_.t1_cookie_timeout); - AdvanceTime(options_.rto_initial); - RunTimers(); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + ASSERT_TRUE(a.options.rto_initial < a.options.t1_cookie_timeout); + AdvanceTime(a, z, a.options.rto_initial); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // When T1-COOKIE expires, both the COOKIE-ECHO and DATA should be present. - AdvanceTime(options_.t1_cookie_timeout - options_.rto_initial); - RunTimers(); + AdvanceTime(a, z, a.options.t1_cookie_timeout - a.options.rto_initial); // And this COOKIE-ECHO and DATA is also lost - never received by Z. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket cookie_echo_packet2, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_THAT(cookie_echo_packet2.descriptors(), SizeIs(2)); EXPECT_EQ(cookie_echo_packet2.descriptors()[0].type, CookieEchoChunk::kType); EXPECT_EQ(cookie_echo_packet2.descriptors()[1].type, DataChunk::kType); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // COOKIE_ECHO has exponential backoff. - AdvanceTime(options_.t1_cookie_timeout * 2); - RunTimers(); + AdvanceTime(a, z, a.options.t1_cookie_timeout * 2); // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); - EXPECT_THAT(cb_z_.ConsumeReceivedMessage()->payload(), + ExchangeMessages(a, z); + EXPECT_THAT(z.cb.ConsumeReceivedMessage()->payload(), SizeIs(kLargeMessageSize)); } TEST_P(DcSctpSocketParametrizedTest, ShutdownConnection) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); RTC_LOG(LS_INFO) << "Shutting down"; - EXPECT_CALL(cb_z_, OnClosed).Times(1); - sock_a_->Shutdown(); + EXPECT_CALL(z->cb, OnClosed).Times(1); + a.socket.Shutdown(); // Z reads SHUTDOWN, produces SHUTDOWN_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // A reads SHUTDOWN_ACK, produces SHUTDOWN_COMPLETE - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Z reads SHUTDOWN_COMPLETE. - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); - EXPECT_EQ(sock_z_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); + EXPECT_EQ(z->socket.state(), SocketState::kClosed); - MaybeHandoverSocketZ(); - EXPECT_EQ(sock_z_->state(), SocketState::kClosed); + z = MaybeHandoverSocket(std::move(z)); + EXPECT_EQ(z->socket.state(), SocketState::kClosed); } -TEST_F(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) { - ConnectSockets(); +TEST(DcSctpSocketTest, ShutdownTimerExpiresTooManyTimeClosesConnection) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); - sock_a_->Shutdown(); + a.socket.Shutdown(); // Drop first SHUTDOWN packet. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); - EXPECT_EQ(sock_a_->state(), SocketState::kShuttingDown); + EXPECT_EQ(a.socket.state(), SocketState::kShuttingDown); - for (int i = 0; i < *options_.max_retransmissions; ++i) { - AdvanceTime(DurationMs(options_.rto_initial * (1 << i))); - RunTimers(); + for (int i = 0; i < *a.options.max_retransmissions; ++i) { + AdvanceTime(a, z, DurationMs(a.options.rto_initial * (1 << i))); // Dropping every shutdown chunk. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(packet.descriptors()[0].type, ShutdownChunk::kType); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); } // The last expiry, makes it abort the connection. - AdvanceTime(options_.rto_initial * (1 << *options_.max_retransmissions)); - EXPECT_CALL(cb_a_, OnAborted).Times(1); - RunTimers(); + EXPECT_CALL(a.cb, OnAborted).Times(1); + AdvanceTime(a, z, + a.options.rto_initial * (1 << *a.options.max_retransmissions)); - EXPECT_EQ(sock_a_->state(), SocketState::kClosed); + EXPECT_EQ(a.socket.state(), SocketState::kClosed); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(packet.descriptors()[0].type, AbortChunk::kType); - EXPECT_TRUE(cb_a_.ConsumeSentPacket().empty()); + EXPECT_TRUE(a.cb.ConsumeSentPacket().empty()); } -TEST_F(DcSctpSocketTest, EstablishConnectionWhileSendingData) { - sock_a_->Connect(); +TEST(DcSctpSocketTest, EstablishConnectionWhileSendingData) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.socket.Connect(); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); // Z reads INIT, produces INIT_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // // A reads INIT_ACK, produces COOKIE_ECHO - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // // Z reads COOKIE_ECHO, produces COOKIE_ACK - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // // A reads COOKIE_ACK. - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - EXPECT_EQ(sock_a_->state(), SocketState::kConnected); - EXPECT_EQ(sock_z_->state(), SocketState::kConnected); + EXPECT_EQ(a.socket.state(), SocketState::kConnected); + EXPECT_EQ(z.socket.state(), SocketState::kConnected); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); } -TEST_F(DcSctpSocketTest, SendMessageAfterEstablished) { - ConnectSockets(); +TEST(DcSctpSocketTest, SendMessageAfterEstablished) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); } TEST_P(DcSctpSocketParametrizedTest, TimeoutResendsPacket) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - cb_a_.ConsumeSentPacket(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.cb.ConsumeSentPacket(); RTC_LOG(LS_INFO) << "Advancing time"; - AdvanceTime(options_.rto_initial); - RunTimers(); + AdvanceTime(a, *z, a.options.rto_initial); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, SendALotOfBytesMissedSecondPacket) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Second DATA (lost) - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); // Retransmit and handle the rest - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); EXPECT_THAT(msg->payload(), testing::ElementsAreArray(payload)); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, SendingHeartbeatAnswersWithAck) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a HEARTBEAT chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); uint8_t info[] = {1, 2, 3, 4}; Parameters::Builder params_builder; params_builder.Add(HeartbeatInfoParameter(info)); b.Add(HeartbeatRequestChunk(params_builder.Build())); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // HEARTBEAT_ACK is sent as a reply. Capture it. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket ack_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); ASSERT_THAT(ack_packet.descriptors(), SizeIs(1)); ASSERT_HAS_VALUE_AND_ASSIGN( HeartbeatAckChunk ack, @@ -844,19 +914,21 @@ TEST_P(DcSctpSocketParametrizedTest, SendingHeartbeatAnswersWithAck) { ASSERT_HAS_VALUE_AND_ASSIGN(HeartbeatInfoParameter info_param, ack.info()); EXPECT_THAT(info_param.info(), ElementsAre(1, 2, 3, 4)); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, ExpectHeartbeatToBeSent) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); - AdvanceTime(options_.heartbeat_interval); - RunTimers(); + AdvanceTime(a, *z, a.options.heartbeat_interval); - std::vector hb_packet_raw = cb_a_.ConsumeSentPacket(); + std::vector hb_packet_raw = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, SctpPacket::Parse(hb_packet_raw)); ASSERT_THAT(hb_packet.descriptors(), SizeIs(1)); @@ -869,86 +941,85 @@ TEST_P(DcSctpSocketParametrizedTest, ExpectHeartbeatToBeSent) { EXPECT_THAT(hb.info()->info(), SizeIs(8)); // Feed it to Sock-z and expect a HEARTBEAT_ACK that will be propagated back. - sock_z_->ReceivePacket(hb_packet_raw); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + z->socket.ReceivePacket(hb_packet_raw); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, CloseConnectionAfterTooManyLostHeartbeats) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_z_, OnClosed).Times(1); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), testing::IsEmpty()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(z->cb, OnClosed).Times(1); + EXPECT_THAT(a.cb.ConsumeSentPacket(), testing::IsEmpty()); // Force-close socket Z so that it doesn't interfere from now on. - sock_z_->Close(); + z->socket.Close(); - DurationMs time_to_next_hearbeat = options_.heartbeat_interval; + DurationMs time_to_next_hearbeat = a.options.heartbeat_interval; - for (int i = 0; i < *options_.max_retransmissions; ++i) { + for (int i = 0; i < *a.options.max_retransmissions; ++i) { RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending..."; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); // Dropping every heartbeat. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(hb_packet.descriptors()[0].type, HeartbeatRequestChunk::kType); RTC_LOG(LS_INFO) << "Letting the heartbeat expire."; - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); - time_to_next_hearbeat = options_.heartbeat_interval - DurationMs(1000); + time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000); } RTC_LOG(LS_INFO) << "Letting HEARTBEAT interval timer expire - sending..."; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); // Last heartbeat - EXPECT_THAT(cb_a_.ConsumeSentPacket(), Not(IsEmpty())); + EXPECT_THAT(a.cb.ConsumeSentPacket(), Not(IsEmpty())); - EXPECT_CALL(cb_a_, OnAborted).Times(1); + EXPECT_CALL(a.cb, OnAborted).Times(1); // Should suffice as exceeding RTO - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); - MaybeHandoverSocketZ(); + z = MaybeHandoverSocket(std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), testing::IsEmpty()); - EXPECT_CALL(cb_z_, OnClosed).Times(1); + EXPECT_THAT(a.cb.ConsumeSentPacket(), testing::IsEmpty()); + EXPECT_CALL(z->cb, OnClosed).Times(1); // Force-close socket Z so that it doesn't interfere from now on. - sock_z_->Close(); + z->socket.Close(); - DurationMs time_to_next_hearbeat = options_.heartbeat_interval; + DurationMs time_to_next_hearbeat = a.options.heartbeat_interval; - for (int i = 0; i < *options_.max_retransmissions; ++i) { - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + for (int i = 0; i < *a.options.max_retransmissions; ++i) { + AdvanceTime(a, *z, time_to_next_hearbeat); // Dropping every heartbeat. - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); RTC_LOG(LS_INFO) << "Letting the heartbeat expire."; - AdvanceTime(DurationMs(1000)); - RunTimers(); + AdvanceTime(a, *z, DurationMs(1000)); - time_to_next_hearbeat = options_.heartbeat_interval - DurationMs(1000); + time_to_next_hearbeat = a.options.heartbeat_interval - DurationMs(1000); } RTC_LOG(LS_INFO) << "Getting the last heartbeat - and acking it"; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); - std::vector hb_packet_raw = cb_a_.ConsumeSentPacket(); + std::vector hb_packet_raw = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket hb_packet, SctpPacket::Parse(hb_packet_raw)); ASSERT_THAT(hb_packet.descriptors(), SizeIs(1)); @@ -956,350 +1027,363 @@ TEST_P(DcSctpSocketParametrizedTest, RecoversAfterASuccessfulAck) { HeartbeatRequestChunk hb, HeartbeatRequestChunk::Parse(hb_packet.descriptors()[0].data)); - SctpPacket::Builder b(sock_a_->verification_tag(), options_); + SctpPacket::Builder b(a.socket.verification_tag(), a.options); b.Add(HeartbeatAckChunk(std::move(hb).extract_parameters())); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // Should suffice as exceeding RTO - which will not fire. - EXPECT_CALL(cb_a_, OnAborted).Times(0); - AdvanceTime(DurationMs(1000)); - RunTimers(); - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_CALL(a.cb, OnAborted).Times(0); + AdvanceTime(a, *z, DurationMs(1000)); + + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); // Verify that we get new heartbeats again. RTC_LOG(LS_INFO) << "Expecting a new heartbeat"; - AdvanceTime(time_to_next_hearbeat); - RunTimers(); + AdvanceTime(a, *z, time_to_next_hearbeat); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket another_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); EXPECT_EQ(another_packet.descriptors()[0].type, HeartbeatRequestChunk::kType); } TEST_P(DcSctpSocketParametrizedTest, ResetStream) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), {}); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - absl::optional msg = cb_z_.ConsumeReceivedMessage(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), {}); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Reset the outgoing stream. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(1)})); // Receiving the packet will trigger a callback, indicating that A has // reset its stream. It will also send a RE-CONFIG with a response. - EXPECT_CALL(cb_z_, OnIncomingStreamsReset).Times(1); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + EXPECT_CALL(z->cb, OnIncomingStreamsReset).Times(1); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Receiving a response will trigger a callback. Streams are now reset. - EXPECT_CALL(cb_a_, OnStreamsResetPerformed).Times(1); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + EXPECT_CALL(a.cb, OnStreamsResetPerformed).Times(1); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, ResetStreamWillMakeChunksStartAtZeroSsn) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - std::vector payload(options_.mtu - 100); + std::vector payload(a.options.mtu - 100); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - auto packet1 = cb_a_.ConsumeSentPacket(); + auto packet1 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet1); + z->socket.ReceivePacket(packet1); - auto packet2 = cb_a_.ConsumeSentPacket(); + auto packet2 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet2, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet2); + z->socket.ReceivePacket(packet2); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->stream_id(), StreamID(1)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->stream_id(), StreamID(1)); // Reset the outgoing stream. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(1)})); // RE-CONFIG, req - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // RE-CONFIG, resp - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - auto packet3 = cb_a_.ConsumeSentPacket(); + auto packet3 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet3, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet3); + z->socket.ReceivePacket(packet3); - auto packet4 = cb_a_.ConsumeSentPacket(); + auto packet4 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet4, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet4); + z->socket.ReceivePacket(packet4); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, ResetStreamWillOnlyResetTheRequestedStreams) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - std::vector payload(options_.mtu - 100); + std::vector payload(a.options.mtu - 100); // Send two ordered messages on SID 1 - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - auto packet1 = cb_a_.ConsumeSentPacket(); + auto packet1 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet1, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet1); + z->socket.ReceivePacket(packet1); - auto packet2 = cb_a_.ConsumeSentPacket(); + auto packet2 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet1, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet2, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet2); + z->socket.ReceivePacket(packet2); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Do the same, for SID 3 - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - auto packet3 = cb_a_.ConsumeSentPacket(); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + auto packet3 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet3, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet3, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(packet3); - auto packet4 = cb_a_.ConsumeSentPacket(); + z->socket.ReceivePacket(packet3); + auto packet4 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet4, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet4, HasDataChunkWithSsn(SSN(1))); - sock_z_->ReceivePacket(packet4); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + z->socket.ReceivePacket(packet4); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Receive all messages. - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->stream_id(), StreamID(1)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->stream_id(), StreamID(1)); - absl::optional msg3 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg3 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg3.has_value()); EXPECT_EQ(msg3->stream_id(), StreamID(3)); - absl::optional msg4 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg4 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg4.has_value()); EXPECT_EQ(msg4->stream_id(), StreamID(3)); // Reset SID 1. This will directly send a RE-CONFIG. - sock_a_->ResetStreams(std::vector({StreamID(3)})); + a.socket.ResetStreams(std::vector({StreamID(3)})); // RE-CONFIG, req - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // RE-CONFIG, resp - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Send a message on SID 1 and 3 - SID 1 should not be reset, but 3 should. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), {}); - sock_a_->Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), payload), {}); - auto packet5 = cb_a_.ConsumeSentPacket(); + auto packet5 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet5, HasDataChunkWithStreamId(StreamID(1))); EXPECT_THAT(packet5, HasDataChunkWithSsn(SSN(2))); // Unchanged. - sock_z_->ReceivePacket(packet5); + z->socket.ReceivePacket(packet5); - auto packet6 = cb_a_.ConsumeSentPacket(); + auto packet6 = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet6, HasDataChunkWithStreamId(StreamID(3))); EXPECT_THAT(packet6, HasDataChunkWithSsn(SSN(0))); // Reset. - sock_z_->ReceivePacket(packet6); + z->socket.ReceivePacket(packet6); // Handle SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, OnePeerReconnects) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnConnectionRestarted).Times(1); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnConnectionRestarted).Times(1); // Let's be evil here - reconnect while a fragmented packet was about to be // sent. The receiving side should get it in full. std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Create a new association, z2 - and don't use z anymore. - testing::NiceMock cb_z2("Z2"); - DcSctpSocket sock_z2("Z2", cb_z2, nullptr, options_); - - sock_z2.Connect(); + SocketUnderTest z2("Z2"); + z2.socket.Connect(); // Retransmit and handle the rest. As there will be some chunks in-flight that // have the wrong verification tag, those will yield errors. - ExchangeMessages(*sock_a_, cb_a_, sock_z2, cb_z2); + ExchangeMessages(a, z2); - absl::optional msg = cb_z2.ConsumeReceivedMessage(); + absl::optional msg = z2.cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); EXPECT_EQ(msg->stream_id(), StreamID(1)); EXPECT_THAT(msg->payload(), testing::ElementsAreArray(payload)); } TEST_P(DcSctpSocketParametrizedTest, SendMessageWithLimitedRtx) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions send_options; send_options.max_retransmissions = 0; - std::vector payload(options_.mtu - 100); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); + std::vector payload(a.options.mtu - 100); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); // First DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Second DATA (lost) - cb_a_.ConsumeSentPacket(); + a.cb.ConsumeSentPacket(); // Third DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Handle SACK for first DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Handle delayed SACK for third DATA - AdvanceTime(options_.delayed_ack_max_timeout); - RunTimers(); + AdvanceTime(a, *z, a.options.delayed_ack_max_timeout); // Handle SACK for second DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // Now the missing data chunk will be marked as nacked, but it might still be // in-flight and the reported gap could be due to out-of-order delivery. So // the RetransmissionQueue will not mark it as "to be retransmitted" until // after the t3-rtx timer has expired. - AdvanceTime(options_.rto_initial); - RunTimers(); + AdvanceTime(a, *z, a.options.rto_initial); // The chunk will be marked as retransmitted, and then as abandoned, which // will trigger a FORWARD-TSN to be sent. // FORWARD-TSN (third) - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); // Which will trigger a SACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->ppid(), PPID(51)); - absl::optional msg2 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg2 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg2.has_value()); EXPECT_EQ(msg2->ppid(), PPID(53)); - absl::optional msg3 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg3 = z->cb.ConsumeReceivedMessage(); EXPECT_FALSE(msg3.has_value()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, SendManyFragmentedMessagesWithLimitedRtx) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions send_options; send_options.unordered = IsUnordered(true); send_options.max_retransmissions = 0; - std::vector payload(options_.mtu * 2 - 100 /* margin */); + std::vector payload(a.options.mtu * 2 - 100 /* margin */); // Sending first message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); // Sending second message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); // Sending third message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), send_options); // Sending fourth message - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(54), payload), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(54), payload), send_options); // First DATA, first fragment - std::vector packet = cb_a_.ConsumeSentPacket(); + std::vector packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(51))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // First DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(51))); // Second DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(52))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Second DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(52))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); // Third DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(53))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Third DATA, second fragment (lost) - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(53))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); // Fourth DATA, first fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(54))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); // Fourth DATA, second fragment - packet = cb_a_.ConsumeSentPacket(); + packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, HasDataChunkWithPPID(PPID(54))); EXPECT_THAT(packet, HasDataChunkWithSsn(SSN(0))); - sock_z_->ReceivePacket(std::move(packet)); + z->socket.ReceivePacket(std::move(packet)); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); // Let the RTX timer expire, and exchange FORWARD-TSN/SACKs - AdvanceTime(options_.rto_initial); - RunTimers(); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + AdvanceTime(a, *z, a.options.rto_initial); + + ExchangeMessages(a, *z); - absl::optional msg1 = cb_z_.ConsumeReceivedMessage(); + absl::optional msg1 = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg1.has_value()); EXPECT_EQ(msg1->ppid(), PPID(54)); - ASSERT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + ASSERT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } struct FakeChunkConfig : ChunkConfig { @@ -1322,17 +1406,20 @@ class FakeChunk : public Chunk, public TLVTrait { }; TEST_P(DcSctpSocketParametrizedTest, ReceivingUnknownChunkRespondsWithError) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a FAKE chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); b.Add(FakeChunk()); - sock_a_->ReceivePacket(b.Build()); + a.socket.ReceivePacket(b.Build()); // ERROR is sent as a reply. Capture it. ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket reply_packet, - SctpPacket::Parse(cb_a_.ConsumeSentPacket())); + SctpPacket::Parse(a.cb.ConsumeSentPacket())); ASSERT_THAT(reply_packet.descriptors(), SizeIs(1)); ASSERT_HAS_VALUE_AND_ASSIGN( ErrorChunk error, ErrorChunk::Parse(reply_packet.descriptors()[0].data)); @@ -1341,50 +1428,52 @@ TEST_P(DcSctpSocketParametrizedTest, ReceivingUnknownChunkRespondsWithError) { error.error_causes().get()); EXPECT_THAT(cause.unrecognized_chunk(), ElementsAre(0x49, 0x00, 0x00, 0x04)); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, ReceivingErrorChunkReportsAsCallback) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Inject a ERROR chunk - SctpPacket::Builder b(sock_a_->verification_tag(), DcSctpOptions()); + SctpPacket::Builder b(a.socket.verification_tag(), DcSctpOptions()); b.Add( ErrorChunk(Parameters::Builder() .Add(UnrecognizedChunkTypeCause({0x49, 0x00, 0x00, 0x04})) .Build())); - EXPECT_CALL(cb_a_, OnError(ErrorKind::kPeerReported, - HasSubstr("Unrecognized Chunk Type"))); - sock_a_->ReceivePacket(b.Build()); + EXPECT_CALL(a.cb, OnError(ErrorKind::kPeerReported, + HasSubstr("Unrecognized Chunk Type"))); + a.socket.ReceivePacket(b.Build()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { - // Create a new association, z2 - and don't use z anymore. - testing::NiceMock cb_z2("Z2"); - DcSctpOptions options = options_; +TEST(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { + SocketUnderTest a("A"); + constexpr size_t kReceiveWindowBufferSize = 2000; - options.max_receiver_window_buffer_size = kReceiveWindowBufferSize; - options.mtu = 3000; - DcSctpSocket sock_z2("Z2", cb_z2, nullptr, options); + SocketUnderTest z( + "Z", {.mtu = 3000, + .max_receiver_window_buffer_size = kReceiveWindowBufferSize}); - EXPECT_CALL(cb_z2, OnClosed).Times(0); - EXPECT_CALL(cb_z2, OnAborted).Times(0); + EXPECT_CALL(z.cb, OnClosed).Times(0); + EXPECT_CALL(z.cb, OnAborted).Times(0); - sock_a_->Connect(); - std::vector init_data = cb_a_.ConsumeSentPacket(); + a.socket.Connect(); + std::vector init_data = a.cb.ConsumeSentPacket(); ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, SctpPacket::Parse(init_data)); ASSERT_HAS_VALUE_AND_ASSIGN( InitChunk init_chunk, InitChunk::Parse(init_packet.descriptors()[0].data)); - sock_z2.ReceivePacket(init_data); - sock_a_->ReceivePacket(cb_z2.ConsumeSentPacket()); - sock_z2.ReceivePacket(cb_a_.ConsumeSentPacket()); - sock_a_->ReceivePacket(cb_z2.ConsumeSentPacket()); + z.socket.ReceivePacket(init_data); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // Fill up Z2 to the high watermark limit. constexpr size_t kWatermarkLimit = @@ -1394,99 +1483,105 @@ TEST_F(DcSctpSocketTest, PassingHighWatermarkWillOnlyAcceptCumAckTsn) { TSN tsn = init_chunk.initial_tsn(); AnyDataChunk::Options opts; opts.is_beginning = Data::IsBeginning(true); - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(tsn, StreamID(1), SSN(0), PPID(53), std::vector(kWatermarkLimit + 1), opts)) .Build()); // First DATA will always trigger a SACK. It's not interesting. - EXPECT_THAT(cb_z2.ConsumeSentPacket(), + EXPECT_THAT(z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(tsn), HasSackWithNoGapAckBlocks())); // This DATA should be accepted - it's advancing cum ack tsn. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 1), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 1), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // The receiver might have moved into delayed ack mode. - cb_z2.AdvanceTime(options.rto_initial); - RunTimers(cb_z2, sock_z2); + AdvanceTime(a, z, z.options.rto_initial); EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA will not be accepted - it's not advancing cum ack tsn. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // Sack will be sent in IMMEDIATE mode when this is happening. EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA will not be accepted either. - sock_z2.ReceivePacket(SctpPacket::Builder(sock_z2.verification_tag(), options) - .Add(DataChunk(AddTo(tsn, 4), StreamID(1), SSN(0), - PPID(53), std::vector(1), - /*options=*/{})) - .Build()); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(AddTo(tsn, 4), StreamID(1), SSN(0), PPID(53), + std::vector(1), + /*options=*/{})) + .Build()); // Sack will be sent in IMMEDIATE mode when this is happening. EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 1)), HasSackWithNoGapAckBlocks())); // This DATA should be accepted, and it fills the reassembly queue. - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(AddTo(tsn, 2), StreamID(1), SSN(0), PPID(53), std::vector(kRemainingSize), /*options=*/{})) .Build()); // The receiver might have moved into delayed ack mode. - cb_z2.AdvanceTime(options.rto_initial); - RunTimers(cb_z2, sock_z2); + AdvanceTime(a, z, z.options.rto_initial); EXPECT_THAT( - cb_z2.ConsumeSentPacket(), + z.cb.ConsumeSentPacket(), AllOf(HasSackWithCumAckTsn(AddTo(tsn, 2)), HasSackWithNoGapAckBlocks())); - EXPECT_CALL(cb_z2, OnAborted(ErrorKind::kResourceExhaustion, _)); - EXPECT_CALL(cb_z2, OnClosed).Times(0); + EXPECT_CALL(z.cb, OnAborted(ErrorKind::kResourceExhaustion, _)); + EXPECT_CALL(z.cb, OnClosed).Times(0); // This DATA will make the connection close. It's too full now. - sock_z2.ReceivePacket( - SctpPacket::Builder(sock_z2.verification_tag(), options) + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) .Add(DataChunk(AddTo(tsn, 3), StreamID(1), SSN(0), PPID(53), std::vector(kSmallMessageSize), /*options=*/{})) .Build()); } -TEST_F(DcSctpSocketTest, SetMaxMessageSize) { - sock_a_->SetMaxMessageSize(42u); - EXPECT_EQ(sock_a_->options().max_message_size, 42u); +TEST(DcSctpSocketTest, SetMaxMessageSize) { + SocketUnderTest a("A"); + + a.socket.SetMaxMessageSize(42u); + EXPECT_EQ(a.socket.options().max_message_size, 42u); } TEST_P(DcSctpSocketParametrizedTest, SendsMessagesWithLowLifetime) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // Mock that the time always goes forward. TimeMs now(0); - EXPECT_CALL(cb_a_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); - EXPECT_CALL(cb_z_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); @@ -1499,27 +1594,30 @@ TEST_P(DcSctpSocketParametrizedTest, SendsMessagesWithLowLifetime) { send_options.unordered = IsUnordered((i % 2) == 0); send_options.lifetime = DurationMs(i % 3); // 0, 1, 2 ms - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); } - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); for (int i = 0; i < kIterations; ++i) { - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z->cb.ConsumeReceivedMessage().has_value()); } - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); // Validate that the sockets really make the time move forward. EXPECT_GE(*now, kIterations * 2); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, DiscardsMessagesWithLowLifetimeIfMustBuffer) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); SendOptions lifetime_0; lifetime_0.unordered = IsUnordered(true); @@ -1531,93 +1629,100 @@ TEST_P(DcSctpSocketParametrizedTest, // Mock that the time always goes forward. TimeMs now(0); - EXPECT_CALL(cb_a_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(a.cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); - EXPECT_CALL(cb_z_, TimeMillis).WillRepeatedly([&]() { + EXPECT_CALL(z->cb, TimeMillis).WillRepeatedly([&]() { now += DurationMs(3); return now; }); // Fill up the send buffer with a large message. std::vector payload(kLargeMessageSize); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), payload), kSendOptions); // And queue a few small messages with lifetime=0 or 1 ms - can't be sent. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), lifetime_0); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {4, 5, 6}), lifetime_1); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {7, 8, 9}), lifetime_0); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), lifetime_0); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {4, 5, 6}), lifetime_1); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {7, 8, 9}), lifetime_0); // Handle all that was sent until congestion window got full. for (;;) { - std::vector packet_from_a = cb_a_.ConsumeSentPacket(); + std::vector packet_from_a = a.cb.ConsumeSentPacket(); if (packet_from_a.empty()) { break; } - sock_z_->ReceivePacket(std::move(packet_from_a)); + z->socket.ReceivePacket(std::move(packet_from_a)); } // Shouldn't be enough to send that large message. - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); // Exchange the rest of the messages, with the time ever increasing. - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); // The large message should be delivered. It was sent reliably. - ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage m1, cb_z_.ConsumeReceivedMessage()); + ASSERT_HAS_VALUE_AND_ASSIGN(DcSctpMessage m1, z->cb.ConsumeReceivedMessage()); EXPECT_EQ(m1.stream_id(), StreamID(1)); EXPECT_THAT(m1.payload(), SizeIs(kLargeMessageSize)); // But none of the smaller messages. - EXPECT_FALSE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, HasReasonableBufferedAmountValues) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_EQ(sock_a_->buffered_amount(StreamID(1)), 0u); + EXPECT_EQ(a.socket.buffered_amount(StreamID(1)), 0u); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kSmallMessageSize)), kSendOptions); // Sending a small message will directly send it as a single packet, so // nothing is left in the queue. - EXPECT_EQ(sock_a_->buffered_amount(StreamID(1)), 0u); + EXPECT_EQ(a.socket.buffered_amount(StreamID(1)), 0u); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions); // Sending a message will directly start sending a few packets, so the // buffered amount is not the full message size. - EXPECT_GT(sock_a_->buffered_amount(StreamID(1)), 0u); - EXPECT_LT(sock_a_->buffered_amount(StreamID(1)), kLargeMessageSize); + EXPECT_GT(a.socket.buffered_amount(StreamID(1)), 0u); + EXPECT_LT(a.socket.buffered_amount(StreamID(1)), kLargeMessageSize); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, HasDefaultOnBufferedAmountLowValueZero) { - EXPECT_EQ(sock_a_->buffered_amount_low_threshold(StreamID(1)), 0u); +TEST(DcSctpSocketTest, HasDefaultOnBufferedAmountLowValueZero) { + SocketUnderTest a("A"); + EXPECT_EQ(a.socket.buffered_amount_low_threshold(StreamID(1)), 0u); } TEST_P(DcSctpSocketParametrizedTest, TriggersOnBufferedAmountLowWithDefaultValueZero) { - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kSmallMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).WillRepeatedly(testing::Return()); - MaybeHandoverSocketZAndSendMessage(); + EXPECT_CALL(a.cb, OnBufferedAmountLow).WillRepeatedly(testing::Return()); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, @@ -1625,64 +1730,70 @@ TEST_P(DcSctpSocketParametrizedTest, static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize * 10; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + a.socket.SetBufferedAmountLowThreshold(StreamID(1), kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); - MaybeHandoverSocketZ(); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(0); - sock_a_->Send( + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(0); + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, TriggersOnBufferedAmountMultipleTimes) { static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize / 2; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + a.socket.SetBufferedAmountLowThreshold(StreamID(1), kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); - MaybeHandoverSocketZ(); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(3); - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(2))).Times(2); - sock_a_->Send( + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(3); + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(2))).Times(2); + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(2), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(2), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, @@ -1690,72 +1801,83 @@ TEST_P(DcSctpSocketParametrizedTest, static constexpr size_t kMessageSize = 1000; static constexpr size_t kBufferedAmountLowThreshold = kMessageSize * 1.5; - sock_a_->SetBufferedAmountLowThreshold(StreamID(1), + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + a.socket.SetBufferedAmountLowThreshold(StreamID(1), kBufferedAmountLowThreshold); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); - ConnectSockets(); - MaybeHandoverSocketZ(); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_CALL(cb_a_, OnBufferedAmountLow).Times(0); + EXPECT_CALL(a.cb, OnBufferedAmountLow).Times(0); // Add a few messages to fill up the congestion window. When that is full, // messages will start to be fully buffered. - while (sock_a_->buffered_amount(StreamID(1)) <= kBufferedAmountLowThreshold) { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + while (a.socket.buffered_amount(StreamID(1)) <= kBufferedAmountLowThreshold) { + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); } - size_t initial_buffered = sock_a_->buffered_amount(StreamID(1)); + size_t initial_buffered = a.socket.buffered_amount(StreamID(1)); ASSERT_GT(initial_buffered, kBufferedAmountLowThreshold); // Start ACKing packets, which will empty the send queue, and trigger the // callback. - EXPECT_CALL(cb_a_, OnBufferedAmountLow(StreamID(1))).Times(1); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnBufferedAmountLow(StreamID(1))).Times(1); + ExchangeMessages(a, *z); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, DoesntTriggerOnTotalBufferAmountLowWhenBelow) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(0); + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(0); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + ExchangeMessages(a, *z); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, TriggersOnTotalBufferAmountLowWhenCrossingThreshold) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(0); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(0); // Fill up the send queue completely. for (;;) { - if (sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + if (a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions) == SendStatus::kErrorResourceExhaustion) { break; } } - EXPECT_CALL(cb_a_, OnTotalBufferedAmountLow).Times(1); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + EXPECT_CALL(a.cb, OnTotalBufferedAmountLow).Times(1); + ExchangeMessages(a, *z); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_F(DcSctpSocketTest, InitialMetricsAreZeroed) { - Metrics metrics = sock_a_->GetMetrics(); +TEST(DcSctpSocketTest, InitialMetricsAreZeroed) { + SocketUnderTest a("A"); + + Metrics metrics = a.socket.GetMetrics(); EXPECT_EQ(metrics.tx_packets_count, 0u); EXPECT_EQ(metrics.tx_messages_count, 0u); EXPECT_EQ(metrics.cwnd_bytes.has_value(), false); @@ -1766,85 +1888,90 @@ TEST_F(DcSctpSocketTest, InitialMetricsAreZeroed) { EXPECT_EQ(metrics.peer_rwnd_bytes.has_value(), false); } -TEST_F(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) { - ConnectSockets(); +TEST(DcSctpSocketTest, RxAndTxPacketMetricsIncrease) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + ConnectSockets(a, z); - const size_t initial_a_rwnd = options_.max_receiver_window_buffer_size * + const size_t initial_a_rwnd = a.options.max_receiver_window_buffer_size * ReassemblyQueue::kHighWatermarkLimit; - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 2u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 2u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 0u); - EXPECT_EQ(*sock_a_->GetMetrics().cwnd_bytes, - options_.cwnd_mtus_initial * options_.mtu); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 0u); + EXPECT_EQ(*a.socket.GetMetrics().cwnd_bytes, + a.options.cwnd_mtus_initial * a.options.mtu); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 2u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 0u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 2u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 0u); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 1u); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 1u); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z.cb.ConsumeReceivedMessage().has_value()); - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 3u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 3u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 1u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 3u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 3u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 1u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 3u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 1u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 3u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 1u); // Send one more (large - fragmented), and receive the delayed SACK. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), - std::vector(options_.mtu * 2 + 1)), + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), + std::vector(a.options.mtu * 2 + 1)), kSendOptions); - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 3u); + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 3u); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 1u); - EXPECT_GT(*sock_a_->GetMetrics().peer_rwnd_bytes, 0u); - EXPECT_LT(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 1u); + EXPECT_GT(*a.socket.GetMetrics().peer_rwnd_bytes, 0u); + EXPECT_LT(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); // DATA + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); // DATA - EXPECT_TRUE(cb_z_.ConsumeReceivedMessage().has_value()); + EXPECT_TRUE(z.cb.ConsumeReceivedMessage().has_value()); - EXPECT_EQ(sock_a_->GetMetrics().tx_packets_count, 6u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 4u); - EXPECT_EQ(sock_a_->GetMetrics().tx_messages_count, 2u); + EXPECT_EQ(a.socket.GetMetrics().tx_packets_count, 6u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 4u); + EXPECT_EQ(a.socket.GetMetrics().tx_messages_count, 2u); - EXPECT_EQ(sock_z_->GetMetrics().rx_packets_count, 6u); - EXPECT_EQ(sock_z_->GetMetrics().rx_messages_count, 2u); + EXPECT_EQ(z.socket.GetMetrics().rx_packets_count, 6u); + EXPECT_EQ(z.socket.GetMetrics().rx_messages_count, 2u); // Delayed sack - AdvanceTime(options_.delayed_ack_max_timeout); - RunTimers(); + AdvanceTime(a, z, a.options.delayed_ack_max_timeout); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // SACK - EXPECT_EQ(sock_a_->GetMetrics().unack_data_count, 0u); - EXPECT_EQ(sock_a_->GetMetrics().rx_packets_count, 5u); - EXPECT_EQ(*sock_a_->GetMetrics().peer_rwnd_bytes, initial_a_rwnd); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); // SACK + EXPECT_EQ(a.socket.GetMetrics().unack_data_count, 0u); + EXPECT_EQ(a.socket.GetMetrics().rx_packets_count, 5u); + EXPECT_EQ(*a.socket.GetMetrics().peer_rwnd_bytes, initial_a_rwnd); } TEST_P(DcSctpSocketParametrizedTest, UnackDataAlsoIncludesSendQueue) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions); size_t payload_bytes = - options_.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; + a.options.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; - size_t expected_sent_packets = options_.cwnd_mtus_initial; + size_t expected_sent_packets = a.options.cwnd_mtus_initial; size_t expected_queued_bytes = kLargeMessageSize - expected_sent_packets * payload_bytes; @@ -1853,42 +1980,48 @@ TEST_P(DcSctpSocketParametrizedTest, UnackDataAlsoIncludesSendQueue) { // Due to alignment, padding etc, it's hard to calculate the exact number, but // it should be in this range. - EXPECT_GE(sock_a_->GetMetrics().unack_data_count, + EXPECT_GE(a.socket.GetMetrics().unack_data_count, expected_sent_packets + expected_queued_packets); - EXPECT_LE(sock_a_->GetMetrics().unack_data_count, + EXPECT_LE(a.socket.GetMetrics().unack_data_count, expected_sent_packets + expected_queued_packets + 2); - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, DoesntSendMoreThanMaxBurstPackets) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), std::vector(kLargeMessageSize)), kSendOptions); for (int i = 0; i < kMaxBurstPackets; ++i) { - std::vector packet = cb_a_.ConsumeSentPacket(); + std::vector packet = a.cb.ConsumeSentPacket(); EXPECT_THAT(packet, Not(IsEmpty())); - sock_z_->ReceivePacket(std::move(packet)); // DATA + z->socket.ReceivePacket(std::move(packet)); // DATA } - EXPECT_THAT(cb_a_.ConsumeSentPacket(), IsEmpty()); + EXPECT_THAT(a.cb.ConsumeSentPacket(), IsEmpty()); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); - MaybeHandoverSocketZAndSendMessage(); + ExchangeMessages(a, *z); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } TEST_P(DcSctpSocketParametrizedTest, SendsOnlyLargePackets) { - ConnectSockets(); - MaybeHandoverSocketZ(); + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); + + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); // A really large message, to ensure that the congestion window is often full. constexpr size_t kMessageSize = 100000; - sock_a_->Send( + a.socket.Send( DcSctpMessage(StreamID(1), PPID(53), std::vector(kMessageSize)), kSendOptions); @@ -1896,21 +2029,21 @@ TEST_P(DcSctpSocketParametrizedTest, SendsOnlyLargePackets) { std::vector data_packet_sizes; do { delivered_packet = false; - std::vector packet_from_a = cb_a_.ConsumeSentPacket(); + std::vector packet_from_a = a.cb.ConsumeSentPacket(); if (!packet_from_a.empty()) { data_packet_sizes.push_back(packet_from_a.size()); delivered_packet = true; - sock_z_->ReceivePacket(std::move(packet_from_a)); + z->socket.ReceivePacket(std::move(packet_from_a)); } - std::vector packet_from_z = cb_z_.ConsumeSentPacket(); + std::vector packet_from_z = z->cb.ConsumeSentPacket(); if (!packet_from_z.empty()) { delivered_packet = true; - sock_a_->ReceivePacket(std::move(packet_from_z)); + a.socket.ReceivePacket(std::move(packet_from_z)); } } while (delivered_packet); size_t packet_payload_bytes = - options_.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; + a.options.mtu - SctpPacket::kHeaderSize - DataChunk::kHeaderSize; // +1 accounts for padding, and rounding up. size_t expected_packets = (kMessageSize + packet_payload_bytes - 1) / packet_payload_bytes + 1; @@ -1922,158 +2055,292 @@ TEST_P(DcSctpSocketParametrizedTest, SendsOnlyLargePackets) { for (size_t size : data_packet_sizes) { // The 4 is for padding/alignment. - EXPECT_GE(size, options_.mtu - 4); + EXPECT_GE(size, a.options.mtu - 4); } - MaybeHandoverSocketZAndSendMessage(); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); } -TEST_P(DcSctpSocketParametrizedTest, DoesntBundleForwardTsnWithData) { - ConnectSockets(); - MaybeHandoverSocketZ(); +TEST(DcSctpSocketTest, SendMessagesAfterHandover) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - // Force an RTT measurement using heartbeats. - AdvanceTime(options_.heartbeat_interval); - RunTimers(); + ConnectSockets(a, *z); - // HEARTBEAT - std::vector hb_req_a = cb_a_.ConsumeSentPacket(); - std::vector hb_req_z = cb_z_.ConsumeSentPacket(); + // Send message before handover to move socket to a not initial state + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); + z->cb.ConsumeReceivedMessage(); - constexpr DurationMs kRtt = DurationMs(80); - AdvanceTime(kRtt); - sock_z_->ReceivePacket(hb_req_a); - sock_a_->ReceivePacket(hb_req_z); + z = HandoverSocket(std::move(z)); - // HEARTBEAT_ACK - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + absl::optional msg; - SendOptions send_options; - send_options.max_retransmissions = 0; - std::vector payload(options_.mtu - 100); + RTC_LOG(LS_INFO) << "Sending A #1"; - // Send an initial message that is received, but the SACK was lost - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - // DATA - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - // SACK (lost) - std::vector sack = cb_z_.ConsumeSentPacket(); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {3, 4}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - // Queue enough messages to fill the congestion window. - do { - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - } while (!cb_a_.ConsumeSentPacket().empty()); + msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(1)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(3, 4)); - // Enqueue at least one more. - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); + RTC_LOG(LS_INFO) << "Sending A #2"; - // Let all of them expire by T3-RTX and inspect what's sent. - AdvanceTime(options_.rto_initial); - RunTimers(); + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {5, 6}), kSendOptions); + z->socket.ReceivePacket(a.cb.ConsumeSentPacket()); - std::vector sent1 = cb_a_.ConsumeSentPacket(); - ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet1, SctpPacket::Parse(sent1)); + msg = z->cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(2)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(5, 6)); + + RTC_LOG(LS_INFO) << "Sending Z #1"; - EXPECT_THAT(packet1.descriptors(), SizeIs(1)); - EXPECT_EQ(packet1.descriptors()[0].type, ForwardTsnChunk::kType); + z->socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), kSendOptions); + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // ack + a.socket.ReceivePacket(z->cb.ConsumeSentPacket()); // data - std::vector sent2 = cb_a_.ConsumeSentPacket(); - ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet2, SctpPacket::Parse(sent2)); + msg = a.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg.has_value()); + EXPECT_EQ(msg->stream_id(), StreamID(1)); + EXPECT_THAT(msg->payload(), testing::ElementsAre(1, 2, 3)); +} - EXPECT_GE(packet2.descriptors().size(), 1u); - EXPECT_EQ(packet2.descriptors()[0].type, DataChunk::kType); +TEST(DcSctpSocketTest, CanDetectDcsctpImplementation) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - // Drop all remaining packets that A has sent. - while (!cb_a_.ConsumeSentPacket().empty()) { - } + ConnectSockets(a, z); - // Replay the SACK, and see if a FORWARD-TSN is sent again. - sock_a_->ReceivePacket(sack); + EXPECT_EQ(a.socket.peer_implementation(), SctpImplementation::kDcsctp); - // It shouldn't be sent as not enough time has passed yet. Instead, more - // DATA chunks are sent, that are in the queue. - std::vector sent3 = cb_a_.ConsumeSentPacket(); - ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet3, SctpPacket::Parse(sent3)); + // As A initiated the connection establishment, Z will not receive enough + // information to know about A's implementation + EXPECT_EQ(z.socket.peer_implementation(), SctpImplementation::kUnknown); +} - EXPECT_GE(packet2.descriptors().size(), 1u); - EXPECT_EQ(packet3.descriptors()[0].type, DataChunk::kType); +TEST(DcSctpSocketTest, BothCanDetectDcsctpImplementation) { + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - // Now let RTT time pass, to allow a FORWARD-TSN to be sent again. - AdvanceTime(kRtt); - sock_a_->ReceivePacket(sack); + EXPECT_CALL(a.cb, OnConnected).Times(1); + EXPECT_CALL(z.cb, OnConnected).Times(1); + a.socket.Connect(); + z.socket.Connect(); - std::vector sent4 = cb_a_.ConsumeSentPacket(); - ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet4, SctpPacket::Parse(sent4)); + ExchangeMessages(a, z); - EXPECT_THAT(packet4.descriptors(), SizeIs(1)); - EXPECT_EQ(packet4.descriptors()[0].type, ForwardTsnChunk::kType); + EXPECT_EQ(a.socket.peer_implementation(), SctpImplementation::kDcsctp); + EXPECT_EQ(z.socket.peer_implementation(), SctpImplementation::kDcsctp); } -TEST_F(DcSctpSocketTest, SendMessagesAfterHandover) { - ConnectSockets(); +TEST_P(DcSctpSocketParametrizedTest, CanLoseFirstOrderedMessage) { + SocketUnderTest a("A"); + auto z = std::make_unique("Z"); - // Send message before handover to move socket to a not initial state - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); - cb_z_.ConsumeReceivedMessage(); + ConnectSockets(a, *z); + z = MaybeHandoverSocket(std::move(z)); - HandoverSocketZ(); + SendOptions send_options; + send_options.unordered = IsUnordered(false); + send_options.max_retransmissions = 0; + std::vector payload(a.options.mtu - 100); - absl::optional msg; + // Send a first message (SID=1, SSN=0) + a.socket.Send(DcSctpMessage(StreamID(1), PPID(51), payload), send_options); - RTC_LOG(LS_INFO) << "Sending A #1"; + // First DATA is lost, and retransmission timer will delete it. + a.cb.ConsumeSentPacket(); + AdvanceTime(a, *z, a.options.rto_initial); + ExchangeMessages(a, *z); - sock_a_->Send(DcSctpMessage(StreamID(1), PPID(53), {3, 4}), kSendOptions); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + // Send a second message (SID=0, SSN=1). + a.socket.Send(DcSctpMessage(StreamID(1), PPID(52), payload), send_options); + ExchangeMessages(a, *z); - msg = cb_z_.ConsumeReceivedMessage(); + // The Z socket should receive the second message, but not the first. + absl::optional msg = z->cb.ConsumeReceivedMessage(); ASSERT_TRUE(msg.has_value()); - EXPECT_EQ(msg->stream_id(), StreamID(1)); - EXPECT_THAT(msg->payload(), testing::ElementsAre(3, 4)); + EXPECT_EQ(msg->ppid(), PPID(52)); - RTC_LOG(LS_INFO) << "Sending A #2"; + EXPECT_FALSE(z->cb.ConsumeReceivedMessage().has_value()); - sock_a_->Send(DcSctpMessage(StreamID(2), PPID(53), {5, 6}), kSendOptions); - sock_z_->ReceivePacket(cb_a_.ConsumeSentPacket()); + MaybeHandoverSocketAndSendMessage(a, std::move(z)); +} - msg = cb_z_.ConsumeReceivedMessage(); - ASSERT_TRUE(msg.has_value()); - EXPECT_EQ(msg->stream_id(), StreamID(2)); - EXPECT_THAT(msg->payload(), testing::ElementsAre(5, 6)); +TEST(DcSctpSocketTest, ReceiveBothUnorderedAndOrderedWithSameTSN) { + /* This issue was found by fuzzing. */ + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - RTC_LOG(LS_INFO) << "Sending Z #1"; + a.socket.Connect(); + std::vector init_data = a.cb.ConsumeSentPacket(); + ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket init_packet, + SctpPacket::Parse(init_data)); + ASSERT_HAS_VALUE_AND_ASSIGN( + InitChunk init_chunk, + InitChunk::Parse(init_packet.descriptors()[0].data)); + z.socket.ReceivePacket(init_data); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); + z.socket.ReceivePacket(a.cb.ConsumeSentPacket()); + a.socket.ReceivePacket(z.cb.ConsumeSentPacket()); - sock_z_->Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2, 3}), kSendOptions); - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // ack - sock_a_->ReceivePacket(cb_z_.ConsumeSentPacket()); // data + // Receive a short unordered message with tsn=INITIAL_TSN+1 + TSN tsn = init_chunk.initial_tsn(); + AnyDataChunk::Options opts; + opts.is_beginning = Data::IsBeginning(true); + opts.is_end = Data::IsEnd(true); + opts.is_unordered = IsUnordered(true); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(TSN(*tsn + 1), StreamID(1), SSN(0), PPID(53), + std::vector(10), opts)) + .Build()); - msg = cb_a_.ConsumeReceivedMessage(); - ASSERT_TRUE(msg.has_value()); - EXPECT_EQ(msg->stream_id(), StreamID(1)); - EXPECT_THAT(msg->payload(), testing::ElementsAre(1, 2, 3)); + // Now receive a longer _ordered_ message with [INITIAL_TSN, INITIAL_TSN+1]. + // This isn't allowed as it reuses TSN=53 with different properties, but it + // shouldn't cause any issues. + opts.is_unordered = IsUnordered(false); + opts.is_end = Data::IsEnd(false); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(tsn, StreamID(1), SSN(0), PPID(53), + std::vector(10), opts)) + .Build()); + + opts.is_beginning = Data::IsBeginning(false); + opts.is_end = Data::IsEnd(true); + z.socket.ReceivePacket( + SctpPacket::Builder(z.socket.verification_tag(), z.options) + .Add(DataChunk(TSN(*tsn + 1), StreamID(1), SSN(0), PPID(53), + std::vector(10), opts)) + .Build()); } -TEST_F(DcSctpSocketTest, CanDetectDcsctpImplementation) { - ConnectSockets(); +TEST(DcSctpSocketTest, CloseTwoStreamsAtTheSameTime) { + // Reported as https://crbug.com/1312009. + SocketUnderTest a("A"); + SocketUnderTest z("Z"); - EXPECT_EQ(sock_a_->peer_implementation(), SctpImplementation::kDcsctp); + EXPECT_CALL(z.cb, OnIncomingStreamsReset(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(z.cb, OnIncomingStreamsReset(ElementsAre(StreamID(2)))).Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed(ElementsAre(StreamID(2)))).Times(1); - // As A initiated the connection establishment, Z will not receive enough - // information to know about A's implementation - EXPECT_EQ(sock_z_->peer_implementation(), SctpImplementation::kUnknown); + ConnectSockets(a, z); + + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {1, 2}), kSendOptions); + + ExchangeMessages(a, z); + + a.socket.ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(2)})); + + ExchangeMessages(a, z); } -TEST_F(DcSctpSocketTest, BothCanDetectDcsctpImplementation) { - EXPECT_CALL(cb_a_, OnConnected).Times(1); - EXPECT_CALL(cb_z_, OnConnected).Times(1); - sock_a_->Connect(); - sock_z_->Connect(); +TEST(DcSctpSocketTest, CloseThreeStreamsAtTheSameTime) { + // Similar to CloseTwoStreamsAtTheSameTime, but ensuring that the two + // remaining streams are reset at the same time in the second request. + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(z.cb, OnIncomingStreamsReset(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(z.cb, OnIncomingStreamsReset( + UnorderedElementsAre(StreamID(2), StreamID(3)))) + .Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed( + UnorderedElementsAre(StreamID(2), StreamID(3)))) + .Times(1); + + ConnectSockets(a, z); - ExchangeMessages(*sock_a_, cb_a_, *sock_z_, cb_z_); + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {1, 2}), kSendOptions); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), {1, 2}), kSendOptions); - EXPECT_EQ(sock_a_->peer_implementation(), SctpImplementation::kDcsctp); - EXPECT_EQ(sock_z_->peer_implementation(), SctpImplementation::kDcsctp); + ExchangeMessages(a, z); + + a.socket.ResetStreams(std::vector({StreamID(1)})); + a.socket.ResetStreams(std::vector({StreamID(2)})); + a.socket.ResetStreams(std::vector({StreamID(3)})); + + ExchangeMessages(a, z); } + +TEST(DcSctpSocketTest, CloseStreamsWithPendingRequest) { + // Checks that stream reset requests are properly paused when they can't be + // immediately reset - i.e. when there is already an ongoing stream reset + // request (and there can only be a single one in-flight). + SocketUnderTest a("A"); + SocketUnderTest z("Z"); + + EXPECT_CALL(z.cb, OnIncomingStreamsReset(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(z.cb, OnIncomingStreamsReset( + UnorderedElementsAre(StreamID(2), StreamID(3)))) + .Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed(ElementsAre(StreamID(1)))).Times(1); + EXPECT_CALL(a.cb, OnStreamsResetPerformed( + UnorderedElementsAre(StreamID(2), StreamID(3)))) + .Times(1); + + ConnectSockets(a, z); + + SendOptions send_options = {.unordered = IsUnordered(false)}; + + // Send a few ordered messages + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), {1, 2}), send_options); + + ExchangeMessages(a, z); + + // Receive these messages + absl::optional msg1 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg1.has_value()); + EXPECT_EQ(msg1->stream_id(), StreamID(1)); + absl::optional msg2 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg2.has_value()); + EXPECT_EQ(msg2->stream_id(), StreamID(2)); + absl::optional msg3 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg3.has_value()); + EXPECT_EQ(msg3->stream_id(), StreamID(3)); + + // Reset the streams - not all at once. + a.socket.ResetStreams(std::vector({StreamID(1)})); + + std::vector packet = a.cb.ConsumeSentPacket(); + EXPECT_THAT(packet, HasReconfigWithStreams(ElementsAre(StreamID(1)))); + z.socket.ReceivePacket(std::move(packet)); + + // Sending more reset requests while this one is ongoing. + + a.socket.ResetStreams(std::vector({StreamID(2)})); + a.socket.ResetStreams(std::vector({StreamID(3)})); + + ExchangeMessages(a, z); + + // Send a few more ordered messages + a.socket.Send(DcSctpMessage(StreamID(1), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(2), PPID(53), {1, 2}), send_options); + a.socket.Send(DcSctpMessage(StreamID(3), PPID(53), {1, 2}), send_options); + + ExchangeMessages(a, z); + + // Receive these messages + absl::optional msg4 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg4.has_value()); + EXPECT_EQ(msg4->stream_id(), StreamID(1)); + absl::optional msg5 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg5.has_value()); + EXPECT_EQ(msg5->stream_id(), StreamID(2)); + absl::optional msg6 = z.cb.ConsumeReceivedMessage(); + ASSERT_TRUE(msg6.has_value()); + EXPECT_EQ(msg6->stream_id(), StreamID(3)); +} // namespace } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h index 1e30777e89..803f688a84 100644 --- a/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h +++ b/net/dcsctp/socket/mock_dcsctp_socket_callbacks.h @@ -153,12 +153,6 @@ class MockDcSctpSocketCallbacks : public DcSctpSocketCallbacks { return timeout_manager_.GetNextExpiredTimeout(); } - void Reset() { - sent_packets_.clear(); - received_messages_.clear(); - timeout_manager_.Reset(); - } - private: const std::string log_prefix_; TimeMs now_ = TimeMs(0); diff --git a/net/dcsctp/socket/stream_reset_handler.cc b/net/dcsctp/socket/stream_reset_handler.cc index 1c6ce09e56..9d86953f44 100644 --- a/net/dcsctp/socket/stream_reset_handler.cc +++ b/net/dcsctp/socket/stream_reset_handler.cc @@ -176,10 +176,10 @@ void StreamResetHandler::HandleResetOutgoing( << "Reset outgoing streams with req_seq_nbr=" << *req->request_sequence_number(); + last_processed_req_seq_nbr_ = req->request_sequence_number(); result = reassembly_queue_->ResetStreams( *req, data_tracker_->last_cumulative_acked_tsn()); if (result == ResponseResult::kSuccessPerformed) { - last_processed_req_seq_nbr_ = req->request_sequence_number(); ctx_->callbacks().OnIncomingStreamsReset(req->stream_ids()); } responses.push_back(ReconfigurationResponseParameter( @@ -270,16 +270,13 @@ absl::optional StreamResetHandler::MakeStreamResetRequest() { // Only send stream resets if there are streams to reset, and no current // ongoing request (there can only be one at a time), and if the stream // can be reset. - if (streams_to_reset_.empty() || current_request_.has_value() || - !retransmission_queue_->CanResetStreams()) { + if (current_request_.has_value() || + !retransmission_queue_->HasStreamsReadyToBeReset()) { return absl::nullopt; } - std::vector streams_to_reset(streams_to_reset_.begin(), - streams_to_reset_.end()); current_request_.emplace(TSN(*retransmission_queue_->next_tsn() - 1), - std::move(streams_to_reset)); - streams_to_reset_.clear(); + retransmission_queue_->GetStreamsReadyToBeReset()); reconfig_timer_->set_duration(ctx_->current_rto()); reconfig_timer_->Start(); return MakeReconfigChunk(); @@ -310,18 +307,8 @@ ReConfigChunk StreamResetHandler::MakeReconfigChunk() { void StreamResetHandler::ResetStreams( rtc::ArrayView outgoing_streams) { - // Enqueue streams to be reset - as this may be called multiple times - // while a request is already in progress (and there can only be one). for (StreamID stream_id : outgoing_streams) { - streams_to_reset_.insert(stream_id); - } - if (current_request_.has_value()) { - // Already an ongoing request - will need to wait for it to finish as - // there can only be one in-flight ReConfig chunk with requests at any - // time. - } else { - retransmission_queue_->PrepareResetStreams(std::vector( - streams_to_reset_.begin(), streams_to_reset_.end())); + retransmission_queue_->PrepareResetStream(stream_id); } } @@ -345,7 +332,7 @@ absl::optional StreamResetHandler::OnReconfigTimerExpiry() { HandoverReadinessStatus StreamResetHandler::GetHandoverReadiness() const { HandoverReadinessStatus status; - if (!streams_to_reset_.empty()) { + if (retransmission_queue_->HasStreamsReadyToBeReset()) { status.Add(HandoverUnreadinessReason::kPendingStreamReset); } if (current_request_.has_value()) { diff --git a/net/dcsctp/socket/stream_reset_handler.h b/net/dcsctp/socket/stream_reset_handler.h index a691eb8312..6e49665538 100644 --- a/net/dcsctp/socket/stream_reset_handler.h +++ b/net/dcsctp/socket/stream_reset_handler.h @@ -216,10 +216,6 @@ class StreamResetHandler { RetransmissionQueue* retransmission_queue_; const std::unique_ptr reconfig_timer_; - // Outgoing streams that have been requested to be reset, but hasn't yet - // been included in an outgoing request. - webrtc::flat_set streams_to_reset_; - // The next sequence number for outgoing stream requests. ReconfigRequestSN next_outgoing_req_seq_nbr_; diff --git a/net/dcsctp/socket/stream_reset_handler_test.cc b/net/dcsctp/socket/stream_reset_handler_test.cc index 6f6874f2a0..a9a8b36bf7 100644 --- a/net/dcsctp/socket/stream_reset_handler_test.cc +++ b/net/dcsctp/socket/stream_reset_handler_test.cc @@ -343,10 +343,13 @@ TEST_F(StreamResetHandlerTest, ResetStreamsDeferred) { } TEST_F(StreamResetHandlerTest, SendOutgoingRequestDirectly) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -360,13 +363,21 @@ TEST_F(StreamResetHandlerTest, SendOutgoingRequestDirectly) { } TEST_F(StreamResetHandlerTest, ResetMultipleStreamsInOneRequest) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(3); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(40))); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(41))); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))).Times(2); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(43))); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(44))); handler_->ResetStreams(std::vector({StreamID(42)})); handler_->ResetStreams( std::vector({StreamID(43), StreamID(44), StreamID(41)})); handler_->ResetStreams(std::vector({StreamID(42), StreamID(40)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return( + std::vector({StreamID(40), StreamID(41), StreamID(42), + StreamID(43), StreamID(44)}))); absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -382,10 +393,10 @@ TEST_F(StreamResetHandlerTest, ResetMultipleStreamsInOneRequest) { } TEST_F(StreamResetHandlerTest, SendOutgoingRequestDeferred) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()) + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()) .WillOnce(Return(false)) .WillOnce(Return(false)) .WillOnce(Return(true)); @@ -396,10 +407,12 @@ TEST_F(StreamResetHandlerTest, SendOutgoingRequestDeferred) { } TEST_F(StreamResetHandlerTest, SendOutgoingResettingOnPositiveResponse) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); @@ -412,8 +425,8 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResettingOnPositiveResponse) { req.request_sequence_number(), ResponseResult::kSuccessPerformed)); ReConfigChunk response_reconfig(builder.Build()); - EXPECT_CALL(producer_, CommitResetStreams()).Times(1); - EXPECT_CALL(producer_, RollbackResetStreams()).Times(0); + EXPECT_CALL(producer_, CommitResetStreams); + EXPECT_CALL(producer_, RollbackResetStreams).Times(0); // Processing a response shouldn't result in sending anything. EXPECT_CALL(callbacks_, OnError).Times(0); @@ -422,10 +435,12 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResettingOnPositiveResponse) { } TEST_F(StreamResetHandlerTest, SendOutgoingResetRollbackOnError) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); @@ -438,8 +453,8 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRollbackOnError) { req.request_sequence_number(), ResponseResult::kErrorBadSequenceNumber)); ReConfigChunk response_reconfig(builder.Build()); - EXPECT_CALL(producer_, CommitResetStreams()).Times(0); - EXPECT_CALL(producer_, RollbackResetStreams()).Times(1); + EXPECT_CALL(producer_, CommitResetStreams).Times(0); + EXPECT_CALL(producer_, RollbackResetStreams); // Only requests should result in sending responses. EXPECT_CALL(callbacks_, OnError).Times(0); @@ -450,10 +465,12 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRollbackOnError) { TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { static constexpr StreamID kStreamToReset = StreamID(42); - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(kStreamToReset)); handler_->ResetStreams(std::vector({kStreamToReset})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({kStreamToReset}))); absl::optional reconfig1 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig1.has_value()); @@ -499,10 +516,13 @@ TEST_F(StreamResetHandlerTest, SendOutgoingResetRetransmitOnInProgress) { } TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); + absl::optional reconfig1 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig1.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -514,6 +534,8 @@ TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { EXPECT_THAT(req1.stream_ids(), UnorderedElementsAre(StreamID(42))); // Streams reset while the request is in-flight will be queued. + EXPECT_CALL(producer_, PrepareResetStream(StreamID(41))); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(43))); StreamID stream_ids[] = {StreamID(41), StreamID(43)}; handler_->ResetStreams(stream_ids); EXPECT_EQ(handler_->MakeStreamResetRequest(), absl::nullopt); @@ -532,7 +554,10 @@ TEST_F(StreamResetHandlerTest, ResetWhileRequestIsSentWillQueue) { handler_->HandleReConfig(std::move(response_reconfig)); // Response has been processed. A new request can be sent. - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(41), StreamID(43)}))); + absl::optional reconfig2 = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig2.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -591,21 +616,31 @@ TEST_F(StreamResetHandlerTest, SendSameRequestTwiceReturnsNothingToDo) { TEST_F(StreamResetHandlerTest, HandoverIsAllowedOnlyWhenNoStreamIsBeingOrWillBeReset) { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); EXPECT_EQ( handler_->GetHandoverReadiness(), HandoverReadinessStatus(HandoverUnreadinessReason::kPendingStreamReset)); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); + ASSERT_TRUE(handler_->MakeStreamResetRequest().has_value()); EXPECT_EQ(handler_->GetHandoverReadiness(), HandoverReadinessStatus( HandoverUnreadinessReason::kPendingStreamResetRequest)); // Reset more streams while the request is in-flight. + EXPECT_CALL(producer_, PrepareResetStream(StreamID(41))); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(43))); StreamID stream_ids[] = {StreamID(41), StreamID(43)}; handler_->ResetStreams(stream_ids); + + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); EXPECT_EQ(handler_->GetHandoverReadiness(), HandoverReadinessStatus() .Add(HandoverUnreadinessReason::kPendingStreamResetRequest) @@ -618,12 +653,18 @@ TEST_F(StreamResetHandlerTest, .Add(ReconfigurationResponseParameter( kMyInitialReqSn, ResponseResult::kSuccessPerformed)) .Build())); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); EXPECT_EQ( handler_->GetHandoverReadiness(), HandoverReadinessStatus(HandoverUnreadinessReason::kPendingStreamReset)); // Second request can be sent. - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(41), StreamID(43)}))); + ASSERT_TRUE(handler_->MakeStreamResetRequest().has_value()); EXPECT_EQ(handler_->GetHandoverReadiness(), HandoverReadinessStatus( @@ -638,16 +679,21 @@ TEST_F(StreamResetHandlerTest, .Build())); // Seconds response has been processed. No pending resets. + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(false)); + EXPECT_TRUE(handler_->GetHandoverReadiness().IsReady()); } TEST_F(StreamResetHandlerTest, HandoverInInitialState) { PerformHandover(); - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); + absl::optional reconfig = handler_->MakeStreamResetRequest(); ASSERT_TRUE(reconfig.has_value()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -663,10 +709,15 @@ TEST_F(StreamResetHandlerTest, HandoverInInitialState) { TEST_F(StreamResetHandlerTest, HandoverAfterHavingResetOneStream) { // Reset one stream { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(42))); handler_->ResetStreams(std::vector({StreamID(42)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()) + .WillOnce(Return(true)) + .WillOnce(Return(false)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(42)}))); + ASSERT_HAS_VALUE_AND_ASSIGN(ReConfigChunk reconfig, handler_->MakeStreamResetRequest()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -690,10 +741,13 @@ TEST_F(StreamResetHandlerTest, HandoverAfterHavingResetOneStream) { // Reset another stream after handover { - EXPECT_CALL(producer_, PrepareResetStreams).Times(1); + EXPECT_CALL(producer_, PrepareResetStream(StreamID(43))); handler_->ResetStreams(std::vector({StreamID(43)})); - EXPECT_CALL(producer_, CanResetStreams()).WillOnce(Return(true)); + EXPECT_CALL(producer_, HasStreamsReadyToBeReset()).WillOnce(Return(true)); + EXPECT_CALL(producer_, GetStreamsReadyToBeReset()) + .WillOnce(Return(std::vector({StreamID(43)}))); + ASSERT_HAS_VALUE_AND_ASSIGN(ReConfigChunk reconfig, handler_->MakeStreamResetRequest()); ASSERT_HAS_VALUE_AND_ASSIGN( @@ -708,5 +762,37 @@ TEST_F(StreamResetHandlerTest, HandoverAfterHavingResetOneStream) { } } +TEST_F(StreamResetHandlerTest, PerformCloseAfterOneFirstFailing) { + // Inject a stream reset on the first expected TSN (which hasn't been seen). + Parameters::Builder builder; + builder.Add(OutgoingSSNResetRequestParameter( + kPeerInitialReqSn, ReconfigRequestSN(3), kPeerInitialTsn, {StreamID(1)})); + + // The socket is expected to say "in progress" as that TSN hasn't been seen. + std::vector responses = + HandleAndCatchResponse(ReConfigChunk(builder.Build())); + EXPECT_THAT(responses, SizeIs(1)); + EXPECT_EQ(responses[0].result(), ResponseResult::kInProgress); + + // Let the socket receive the TSN. + DataGeneratorOptions opts; + opts.message_id = MID(0); + reasm_->Add(kPeerInitialTsn, gen_.Ordered({1, 2, 3, 4}, "BE", opts)); + reasm_->MaybeResetStreamsDeferred(kPeerInitialTsn); + data_tracker_->Observe(kPeerInitialTsn); + + // And emulate that time has passed, and the peer retries the stream reset, + // but now with an incremented request sequence number. + Parameters::Builder builder2; + builder2.Add(OutgoingSSNResetRequestParameter( + ReconfigRequestSN(*kPeerInitialReqSn + 1), ReconfigRequestSN(3), + kPeerInitialTsn, {StreamID(1)})); + + // This is supposed to be handled well. + std::vector responses2 = + HandleAndCatchResponse(ReConfigChunk(builder2.Build())); + EXPECT_THAT(responses2, SizeIs(1)); + EXPECT_EQ(responses2[0].result(), ResponseResult::kSuccessPerformed); +} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/socket/transmission_control_block.cc b/net/dcsctp/socket/transmission_control_block.cc index 0fc0d4c029..78331d5e96 100644 --- a/net/dcsctp/socket/transmission_control_block.cc +++ b/net/dcsctp/socket/transmission_control_block.cc @@ -102,11 +102,35 @@ void TransmissionControlBlock::MaybeSendForwardTsn(SctpPacket::Builder& builder, } } +void TransmissionControlBlock::MaybeSendFastRetransmit() { + if (!retransmission_queue_.has_data_to_be_fast_retransmitted()) { + return; + } + + // https://datatracker.ietf.org/doc/html/rfc4960#section-7.2.4 + // "Determine how many of the earliest (i.e., lowest TSN) DATA chunks marked + // for retransmission will fit into a single packet, subject to constraint of + // the path MTU of the destination transport address to which the packet is + // being sent. Call this value K. Retransmit those K DATA chunks in a single + // packet. When a Fast Retransmit is being performed, the sender SHOULD + // ignore the value of cwnd and SHOULD NOT delay retransmission for this + // single packet." + + SctpPacket::Builder builder(peer_verification_tag_, options_); + auto chunks = retransmission_queue_.GetChunksForFastRetransmit( + builder.bytes_remaining()); + for (auto& [tsn, data] : chunks) { + if (capabilities_.message_interleaving) { + builder.Add(IDataChunk(tsn, std::move(data), false)); + } else { + builder.Add(DataChunk(tsn, std::move(data), false)); + } + } + packet_sender_.Send(builder); +} + void TransmissionControlBlock::SendBufferedPackets(SctpPacket::Builder& builder, TimeMs now) { - // FORWARD-TSNs are sent as separate packets to avoid bugs.webrtc.org/12961. - MaybeSendForwardTsn(builder, now); - for (int packet_idx = 0; packet_idx < options_.max_burst && retransmission_queue_.can_send_data(); ++packet_idx) { @@ -131,6 +155,7 @@ void TransmissionControlBlock::SendBufferedPackets(SctpPacket::Builder& builder, builder.Add(data_tracker_.CreateSelectiveAck( reassembly_queue_.remaining_bytes())); } + MaybeSendForwardTsn(builder, now); absl::optional reconfig = stream_reset_handler_.MakeStreamResetRequest(); if (reconfig.has_value()) { diff --git a/net/dcsctp/socket/transmission_control_block.h b/net/dcsctp/socket/transmission_control_block.h index 8cefbc65f4..8c240f1043 100644 --- a/net/dcsctp/socket/transmission_control_block.h +++ b/net/dcsctp/socket/transmission_control_block.h @@ -183,6 +183,8 @@ class TransmissionControlBlock : public Context { bool has_cookie_echo_chunk() const { return cookie_echo_chunk_.has_value(); } + void MaybeSendFastRetransmit(); + // Fills `builder` (which may already be filled with control chunks) with // other control and data chunks, and sends packets as much as can be // allowed by the congestion control algorithm. diff --git a/net/dcsctp/testing/BUILD.gn b/net/dcsctp/testing/BUILD.gn index 5367ef8c6f..7e005a1f0c 100644 --- a/net/dcsctp/testing/BUILD.gn +++ b/net/dcsctp/testing/BUILD.gn @@ -17,9 +17,7 @@ rtc_library("data_generator") { testonly = true deps = [ "../../../api:array_view", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../common:internal_types", "../packet:data", "../public:types", diff --git a/net/dcsctp/timer/BUILD.gn b/net/dcsctp/timer/BUILD.gn index 4470903ffc..de1261f03a 100644 --- a/net/dcsctp/timer/BUILD.gn +++ b/net/dcsctp/timer/BUILD.gn @@ -12,9 +12,8 @@ rtc_library("timer") { deps = [ "../../../api:array_view", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:strong_alias", "../../../rtc_base/containers:flat_map", "../../../rtc_base/containers:flat_set", "../public:socket", @@ -35,9 +34,8 @@ rtc_library("task_queue_timeout") { deps = [ "../../../api:array_view", "../../../api/task_queue:task_queue", - "../../../rtc_base", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../../../rtc_base/task_utils:pending_task_safety_flag", "../../../rtc_base/task_utils:to_queued_task", "../public:socket", @@ -62,7 +60,6 @@ if (rtc_include_tests) { "../../../api/task_queue/test:mock_task_queue_base", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "../../../test/time_controller:time_controller", "../public:socket", diff --git a/net/dcsctp/timer/fake_timeout.h b/net/dcsctp/timer/fake_timeout.h index f87227577b..74ffe5af29 100644 --- a/net/dcsctp/timer/fake_timeout.h +++ b/net/dcsctp/timer/fake_timeout.h @@ -97,8 +97,6 @@ class FakeTimeoutManager { return absl::nullopt; } - void Reset() { timers_.clear(); } - private: const std::function get_time_; webrtc::flat_set timers_; diff --git a/net/dcsctp/tx/BUILD.gn b/net/dcsctp/tx/BUILD.gn index 44dbd0b8f8..a94c103cb3 100644 --- a/net/dcsctp/tx/BUILD.gn +++ b/net/dcsctp/tx/BUILD.gn @@ -25,7 +25,7 @@ rtc_library("rr_send_queue") { ":send_queue", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../packet:data", "../public:socket", "../public:types", @@ -45,7 +45,7 @@ rtc_library("rr_send_queue") { rtc_library("retransmission_error_counter") { deps = [ "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../public:types", ] sources = [ @@ -58,7 +58,6 @@ rtc_library("retransmission_error_counter") { rtc_library("retransmission_timeout") { deps = [ "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", "../public:types", ] sources = [ @@ -73,7 +72,7 @@ rtc_library("outstanding_data") { ":send_queue", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", "../common:math", "../common:sequence_numbers", "../common:str_join", @@ -101,7 +100,8 @@ rtc_library("retransmission_queue") { ":send_queue", "../../../api:array_view", "../../../rtc_base:checks", - "../../../rtc_base:rtc_base_approved", + "../../../rtc_base:logging", + "../../../rtc_base:stringutils", "../common:math", "../common:sequence_numbers", "../common:str_join", @@ -149,7 +149,6 @@ if (rtc_include_tests) { "../../../api/task_queue:task_queue", "../../../rtc_base:checks", "../../../rtc_base:gunit_helpers", - "../../../rtc_base:rtc_base_approved", "../../../test:test_support", "../common:handover_testing", "../common:math", diff --git a/net/dcsctp/tx/mock_send_queue.h b/net/dcsctp/tx/mock_send_queue.h index 0cf64583ae..82e96b7084 100644 --- a/net/dcsctp/tx/mock_send_queue.h +++ b/net/dcsctp/tx/mock_send_queue.h @@ -11,6 +11,7 @@ #define NET_DCSCTP_TX_MOCK_SEND_QUEUE_H_ #include +#include #include "absl/types/optional.h" #include "api/array_view.h" @@ -35,11 +36,9 @@ class MockSendQueue : public SendQueue { Discard, (IsUnordered unordered, StreamID stream_id, MID message_id), (override)); - MOCK_METHOD(void, - PrepareResetStreams, - (rtc::ArrayView streams), - (override)); - MOCK_METHOD(bool, CanResetStreams, (), (const, override)); + MOCK_METHOD(void, PrepareResetStream, (StreamID stream_id), (override)); + MOCK_METHOD(bool, HasStreamsReadyToBeReset, (), (const, override)); + MOCK_METHOD(std::vector, GetStreamsReadyToBeReset, (), (override)); MOCK_METHOD(void, CommitResetStreams, (), (override)); MOCK_METHOD(void, RollbackResetStreams, (), (override)); MOCK_METHOD(void, Reset, (), (override)); diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc index 05277d0584..c013ac5bdd 100644 --- a/net/dcsctp/tx/outstanding_data.cc +++ b/net/dcsctp/tx/outstanding_data.cc @@ -31,19 +31,19 @@ size_t OutstandingData::GetSerializedChunkSize(const Data& data) const { } void OutstandingData::Item::Ack() { + lifecycle_ = Lifecycle::kActive; ack_state_ = AckState::kAcked; - should_be_retransmitted_ = false; } OutstandingData::Item::NackAction OutstandingData::Item::Nack( bool retransmit_now) { ack_state_ = AckState::kNacked; ++nack_count_; - if ((retransmit_now || nack_count_ >= kNumberOfNacksForRetransmission) && - !is_abandoned_) { + if (!should_be_retransmitted() && !is_abandoned() && + (retransmit_now || nack_count_ >= kNumberOfNacksForRetransmission)) { // Nacked enough times - it's considered lost. if (num_retransmissions_ < *max_retransmissions_) { - should_be_retransmitted_ = true; + lifecycle_ = Lifecycle::kToBeRetransmitted; return NackAction::kRetransmit; } Abandon(); @@ -52,17 +52,16 @@ OutstandingData::Item::NackAction OutstandingData::Item::Nack( return NackAction::kNothing; } -void OutstandingData::Item::Retransmit() { +void OutstandingData::Item::MarkAsRetransmitted() { + lifecycle_ = Lifecycle::kActive; ack_state_ = AckState::kUnacked; - should_be_retransmitted_ = false; nack_count_ = 0; ++num_retransmissions_; } void OutstandingData::Item::Abandon() { - is_abandoned_ = true; - should_be_retransmitted_ = false; + lifecycle_ = Lifecycle::kAbandoned; } bool OutstandingData::Item::has_expired(TimeMs now) const { @@ -73,7 +72,13 @@ bool OutstandingData::IsConsistent() const { size_t actual_outstanding_bytes = 0; size_t actual_outstanding_items = 0; - std::set actual_to_be_retransmitted; + std::set combined_to_be_retransmitted; + combined_to_be_retransmitted.insert(to_be_retransmitted_.begin(), + to_be_retransmitted_.end()); + combined_to_be_retransmitted.insert(to_be_fast_retransmitted_.begin(), + to_be_fast_retransmitted_.end()); + + std::set actual_combined_to_be_retransmitted; for (const auto& [tsn, item] : outstanding_data_) { if (item.is_outstanding()) { actual_outstanding_bytes += GetSerializedChunkSize(item.data()); @@ -81,7 +86,7 @@ bool OutstandingData::IsConsistent() const { } if (item.should_be_retransmitted()) { - actual_to_be_retransmitted.insert(tsn); + actual_combined_to_be_retransmitted.insert(tsn); } } @@ -92,7 +97,7 @@ bool OutstandingData::IsConsistent() const { return actual_outstanding_bytes == outstanding_bytes_ && actual_outstanding_items == outstanding_items_ && - actual_to_be_retransmitted == to_be_retransmitted_; + actual_combined_to_be_retransmitted == combined_to_be_retransmitted; } void OutstandingData::AckChunk(AckInfo& ack_info, @@ -105,6 +110,8 @@ void OutstandingData::AckChunk(AckInfo& ack_info, --outstanding_items_; } if (iter->second.should_be_retransmitted()) { + RTC_DCHECK(to_be_fast_retransmitted_.find(iter->first) == + to_be_fast_retransmitted_.end()); to_be_retransmitted_.erase(iter->first); } iter->second.Ack(); @@ -116,7 +123,7 @@ void OutstandingData::AckChunk(AckInfo& ack_info, OutstandingData::AckInfo OutstandingData::HandleSack( UnwrappedTSN cumulative_tsn_ack, rtc::ArrayView gap_ack_blocks, - bool is_in_fast_retransmit) { + bool is_in_fast_recovery) { OutstandingData::AckInfo ack_info(cumulative_tsn_ack); // Erase all items up to cumulative_tsn_ack. RemoveAcked(cumulative_tsn_ack, ack_info); @@ -125,8 +132,8 @@ OutstandingData::AckInfo OutstandingData::HandleSack( AckGapBlocks(cumulative_tsn_ack, gap_ack_blocks, ack_info); // NACK and possibly mark for retransmit chunks that weren't acked. - NackBetweenAckBlocks(cumulative_tsn_ack, gap_ack_blocks, - is_in_fast_retransmit, ack_info); + NackBetweenAckBlocks(cumulative_tsn_ack, gap_ack_blocks, is_in_fast_recovery, + ack_info); RTC_DCHECK(IsConsistent()); return ack_info; @@ -197,8 +204,9 @@ void OutstandingData::NackBetweenAckBlocks( for (auto iter = outstanding_data_.upper_bound(prev_block_last_acked); iter != outstanding_data_.lower_bound(cur_block_first_acked); ++iter) { if (iter->first <= max_tsn_to_nack) { - ack_info.has_packet_loss = - NackItem(iter->first, iter->second, /*retransmit_now=*/false); + ack_info.has_packet_loss |= + NackItem(iter->first, iter->second, /*retransmit_now=*/false, + /*do_fast_retransmit=*/!is_in_fast_recovery); } } prev_block_last_acked = UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end); @@ -212,7 +220,8 @@ void OutstandingData::NackBetweenAckBlocks( bool OutstandingData::NackItem(UnwrappedTSN tsn, Item& item, - bool retransmit_now) { + bool retransmit_now, + bool do_fast_retransmit) { if (item.is_outstanding()) { outstanding_bytes_ -= GetSerializedChunkSize(item.data()); --outstanding_items_; @@ -222,7 +231,11 @@ bool OutstandingData::NackItem(UnwrappedTSN tsn, case Item::NackAction::kNothing: return false; case Item::NackAction::kRetransmit: - to_be_retransmitted_.insert(tsn); + if (do_fast_retransmit) { + to_be_fast_retransmitted_.insert(tsn); + } else { + to_be_retransmitted_.insert(tsn); + } RTC_DLOG(LS_VERBOSE) << *tsn.Wrap() << " marked for retransmission"; break; case Item::NackAction::kAbandon: @@ -271,6 +284,7 @@ void OutstandingData::AbandonAllFor(const Item& item) { RTC_DLOG(LS_VERBOSE) << "Marking chunk " << *tsn.Wrap() << " as abandoned"; if (other.should_be_retransmitted()) { + to_be_fast_retransmitted_.erase(tsn); to_be_retransmitted_.erase(tsn); } other.Abandon(); @@ -278,12 +292,12 @@ void OutstandingData::AbandonAllFor(const Item& item) { } } -std::vector> OutstandingData::GetChunksToBeRetransmitted( +std::vector> OutstandingData::ExtractChunksThatCanFit( + std::set& chunks, size_t max_size) { std::vector> result; - for (auto it = to_be_retransmitted_.begin(); - it != to_be_retransmitted_.end();) { + for (auto it = chunks.begin(); it != chunks.end();) { UnwrappedTSN tsn = *it; auto elem = outstanding_data_.find(tsn); RTC_DCHECK(elem != outstanding_data_.end()); @@ -295,12 +309,12 @@ std::vector> OutstandingData::GetChunksToBeRetransmitted( size_t serialized_size = GetSerializedChunkSize(item.data()); if (serialized_size <= max_size) { - item.Retransmit(); + item.MarkAsRetransmitted(); result.emplace_back(tsn.Wrap(), item.data().Clone()); max_size -= serialized_size; outstanding_bytes_ += serialized_size; ++outstanding_items_; - it = to_be_retransmitted_.erase(it); + it = chunks.erase(it); } else { ++it; } @@ -309,11 +323,37 @@ std::vector> OutstandingData::GetChunksToBeRetransmitted( break; } } + return result; +} + +std::vector> +OutstandingData::GetChunksToBeFastRetransmitted(size_t max_size) { + std::vector> result = + ExtractChunksThatCanFit(to_be_fast_retransmitted_, max_size); + + // https://datatracker.ietf.org/doc/html/rfc4960#section-7.2.4 + // "Those TSNs marked for retransmission due to the Fast-Retransmit algorithm + // that did not fit in the sent datagram carrying K other TSNs are also marked + // as ineligible for a subsequent Fast Retransmit. However, as they are + // marked for retransmission they will be retransmitted later on as soon as + // cwnd allows." + if (!to_be_fast_retransmitted_.empty()) { + to_be_retransmitted_.insert(to_be_fast_retransmitted_.begin(), + to_be_fast_retransmitted_.end()); + to_be_fast_retransmitted_.clear(); + } RTC_DCHECK(IsConsistent()); return result; } +std::vector> OutstandingData::GetChunksToBeRetransmitted( + size_t max_size) { + // Chunks scheduled for fast retransmission must be sent first. + RTC_DCHECK(to_be_fast_retransmitted_.empty()); + return ExtractChunksThatCanFit(to_be_retransmitted_, max_size); +} + void OutstandingData::ExpireOutstandingChunks(TimeMs now) { for (const auto& [tsn, item] : outstanding_data_) { // Chunks that are nacked can be expired. Care should be taken not to expire @@ -374,7 +414,8 @@ absl::optional OutstandingData::Insert( void OutstandingData::NackAll() { for (auto& [tsn, item] : outstanding_data_) { if (!item.is_acked()) { - NackItem(tsn, item, /*retransmit_now=*/true); + NackItem(tsn, item, /*retransmit_now=*/true, + /*do_fast_retransmit=*/false); } } RTC_DCHECK(IsConsistent()); diff --git a/net/dcsctp/tx/outstanding_data.h b/net/dcsctp/tx/outstanding_data.h index dc9aab7f96..382490b52f 100644 --- a/net/dcsctp/tx/outstanding_data.h +++ b/net/dcsctp/tx/outstanding_data.h @@ -77,7 +77,14 @@ class OutstandingData { AckInfo HandleSack( UnwrappedTSN cumulative_tsn_ack, rtc::ArrayView gap_ack_blocks, - bool is_in_fast_retransmit); + bool is_in_fast_recovery); + + // Returns as many of the chunks that are eligible for fast retransmissions + // and that would fit in a single packet of `max_size`. The eligible chunks + // that didn't fit will be marked for (normal) retransmission and will not be + // returned if this method is called again. + std::vector> GetChunksToBeFastRetransmitted( + size_t max_size); // Given `max_size` of space left in a packet, which chunks can be added to // it? @@ -94,8 +101,12 @@ class OutstandingData { bool empty() const { return outstanding_data_.empty(); } + bool has_data_to_be_fast_retransmitted() const { + return !to_be_fast_retransmitted_.empty(); + } + bool has_data_to_be_retransmitted() const { - return !to_be_retransmitted_.empty(); + return !to_be_retransmitted_.empty() || !to_be_fast_retransmitted_.empty(); } UnwrappedTSN last_cumulative_tsn_ack() const { @@ -167,11 +178,11 @@ class OutstandingData { // is set, it might be marked for retransmission. If the item has reached // its max retransmission value, it will instead be abandoned. The action // performed is indicated as return value. - NackAction Nack(bool retransmit_now = false); + NackAction Nack(bool retransmit_now); // Prepares the item to be retransmitted. Sets it as outstanding and // clears all nack counters. - void Retransmit(); + void MarkAsRetransmitted(); // Marks this item as abandoned. void Abandon(); @@ -179,10 +190,12 @@ class OutstandingData { bool is_outstanding() const { return ack_state_ == AckState::kUnacked; } bool is_acked() const { return ack_state_ == AckState::kAcked; } bool is_nacked() const { return ack_state_ == AckState::kNacked; } - bool is_abandoned() const { return is_abandoned_; } + bool is_abandoned() const { return lifecycle_ == Lifecycle::kAbandoned; } // Indicates if this chunk should be retransmitted. - bool should_be_retransmitted() const { return should_be_retransmitted_; } + bool should_be_retransmitted() const { + return lifecycle_ == Lifecycle::kToBeRetransmitted; + } // Indicates if this chunk has ever been retransmitted. bool has_been_retransmitted() const { return num_retransmissions_ > 0; } @@ -191,18 +204,28 @@ class OutstandingData { bool has_expired(TimeMs now) const; private: + enum class Lifecycle { + // The chunk is alive (sent, received, etc) + kActive, + // The chunk is scheduled to be retransmitted, and will then transition to + // become active. + kToBeRetransmitted, + // The chunk has been abandoned. This is a terminal state. + kAbandoned + }; enum class AckState { + // The chunk is in-flight. kUnacked, + // The chunk has been received and acknowledged. kAcked, - kNacked, + // The chunk has been nacked and is possibly lost. + kNacked }; + // Indicates the life cycle status of this chunk. + Lifecycle lifecycle_ = Lifecycle::kActive; // Indicates the presence of this chunk, if it's in flight (Unacked), has - // been received (Acked) or is lost (Nacked). + // been received (Acked) or is possibly lost (Nacked). AckState ack_state_ = AckState::kUnacked; - // Indicates if this chunk has been abandoned, which is a terminal state. - bool is_abandoned_ = false; - // Indicates if this chunk should be retransmitted. - bool should_be_retransmitted_ = false; // The number of times the DATA chunk has been nacked (by having received a // SACK which doesn't include it). Will be cleared on retransmissions. @@ -246,21 +269,32 @@ class OutstandingData { bool is_in_fast_recovery, OutstandingData::AckInfo& ack_info); - // Acks the chunk referenced by `iter` and updates state in `ack_info` and the - // object's state. + // Process the acknowledgement of the chunk referenced by `iter` and updates + // state in `ack_info` and the object's state. void AckChunk(AckInfo& ack_info, std::map::iterator iter); - // Helper method to nack an item and perform the correct operations given the - // action indicated when nacking an item (e.g. retransmitting or abandoning). - // The return value indicate if an action was performed, meaning that packet - // loss was detected and acted upon. - bool NackItem(UnwrappedTSN tsn, Item& item, bool retransmit_now); + // Helper method to process an incoming nack of an item and perform the + // correct operations given the action indicated when nacking an item (e.g. + // retransmitting or abandoning). The return value indicate if an action was + // performed, meaning that packet loss was detected and acted upon. If + // `do_fast_retransmit` is set and if the item has been nacked sufficiently + // many times so that it should be retransmitted, this will schedule it to be + // "fast retransmitted". This is only done just before going into fast + // recovery. + bool NackItem(UnwrappedTSN tsn, + Item& item, + bool retransmit_now, + bool do_fast_retransmit); // Given that a message fragment, `item` has been abandoned, abandon all other // fragments that share the same message - both never-before-sent fragments // that are still in the SendQueue and outstanding chunks. void AbandonAllFor(const OutstandingData::Item& item); + std::vector> ExtractChunksThatCanFit( + std::set& chunks, + size_t max_size); + bool IsConsistent() const; // The size of the data chunk (DATA/I-DATA) header that is used. @@ -278,6 +312,8 @@ class OutstandingData { // The number of DATA chunks that are in-flight (sent but not yet acked or // nacked). size_t outstanding_items_ = 0; + // Data chunks that are eligible for fast retransmission. + std::set to_be_fast_retransmitted_; // Data chunks that are to be retransmitted. std::set to_be_retransmitted_; }; diff --git a/net/dcsctp/tx/outstanding_data_test.cc b/net/dcsctp/tx/outstanding_data_test.cc index c161cbb6da..113f5f44e4 100644 --- a/net/dcsctp/tx/outstanding_data_test.cc +++ b/net/dcsctp/tx/outstanding_data_test.cc @@ -28,6 +28,7 @@ using ::testing::MockFunction; using State = ::dcsctp::OutstandingData::State; using ::testing::_; using ::testing::ElementsAre; +using ::testing::IsEmpty; using ::testing::Pair; using ::testing::Return; using ::testing::StrictMock; @@ -203,8 +204,9 @@ TEST_F(OutstandingDataTest, NacksThreeTimesResultsInRetransmission) { Pair(TSN(12), State::kAcked), // Pair(TSN(13), State::kAcked))); - EXPECT_THAT(buf_.GetChunksToBeRetransmitted(1000), + EXPECT_THAT(buf_.GetChunksToBeFastRetransmitted(1000), ElementsAre(Pair(TSN(10), _))); + EXPECT_THAT(buf_.GetChunksToBeRetransmitted(1000), IsEmpty()); } TEST_F(OutstandingDataTest, NacksThreeTimesResultsInAbandoning) { @@ -397,5 +399,125 @@ TEST_F(OutstandingDataTest, MeasureRTT) { EXPECT_EQ(duration, kDuration - DurationMs(1)); } +TEST_F(OutstandingDataTest, MustRetransmitBeforeGettingNackedAgain) { + // This test case verifies that a chunk that has been nacked, and scheduled to + // be retransmitted, doesn't get nacked again until it has been actually sent + // on the wire. + + static constexpr MaxRetransmits kOneRetransmission(1); + for (int tsn = 10; tsn <= 20; ++tsn) { + buf_.Insert(gen_.Ordered({1}, tsn == 10 ? "B" : tsn == 20 ? "E" : ""), + kOneRetransmission, kNow, TimeMs::InfiniteFuture()); + } + + std::vector gab1 = {SackChunk::GapAckBlock(2, 2)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab2 = {SackChunk::GapAckBlock(2, 3)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab2, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab3 = {SackChunk::GapAckBlock(2, 4)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab3, false); + EXPECT_TRUE(ack.has_packet_loss); + EXPECT_TRUE(buf_.has_data_to_be_retransmitted()); + + // Don't call GetChunksToBeRetransmitted yet - simulate that the congestion + // window doesn't allow it to be retransmitted yet. It does however get more + // SACKs indicating packet loss. + + std::vector gab4 = {SackChunk::GapAckBlock(2, 5)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab4, false).has_packet_loss); + EXPECT_TRUE(buf_.has_data_to_be_retransmitted()); + + std::vector gab5 = {SackChunk::GapAckBlock(2, 6)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab5, false).has_packet_loss); + EXPECT_TRUE(buf_.has_data_to_be_retransmitted()); + + std::vector gab6 = {SackChunk::GapAckBlock(2, 7)}; + OutstandingData::AckInfo ack2 = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab6, false); + + EXPECT_FALSE(ack2.has_packet_loss); + EXPECT_TRUE(buf_.has_data_to_be_retransmitted()); + + // Now it's retransmitted. + EXPECT_THAT(buf_.GetChunksToBeFastRetransmitted(1000), + ElementsAre(Pair(TSN(10), _))); + EXPECT_THAT(buf_.GetChunksToBeRetransmitted(1000), IsEmpty()); + + // And obviously lost, as it will get NACKed and abandoned. + std::vector gab7 = {SackChunk::GapAckBlock(2, 8)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab7, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + std::vector gab8 = {SackChunk::GapAckBlock(2, 9)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab8, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(false)); + + std::vector gab9 = {SackChunk::GapAckBlock(2, 10)}; + OutstandingData::AckInfo ack3 = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab9, false); + + EXPECT_TRUE(ack3.has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); +} + +TEST_F(OutstandingDataTest, CanAbandonChunksMarkedForFastRetransmit) { + // This test is a bit convoluted, and can't really happen with a well behaving + // client, but this was found by fuzzers. This test will verify that a message + // that was both marked as "to be fast retransmitted" and "abandoned" at the + // same time doesn't cause any consistency issues. + + // Add chunks 10-14, but chunk 11 has zero retransmissions. When chunk 10 and + // 11 are NACKed three times, chunk 10 will be marked for retransmission, but + // chunk 11 will be abandoned, which also abandons chunk 10, as it's part of + // the same message. + buf_.Insert(gen_.Ordered({1}, "B"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); // 10 + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits(0), kNow, + TimeMs::InfiniteFuture()); // 11 + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); // 12 + buf_.Insert(gen_.Ordered({1}, ""), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); // 13 + buf_.Insert(gen_.Ordered({1}, "E"), MaxRetransmits::NoLimit(), kNow, + TimeMs::InfiniteFuture()); // 13 + + // ACK 9, 12 + std::vector gab1 = {SackChunk::GapAckBlock(3, 3)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab1, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + // ACK 9, 12, 13 + std::vector gab2 = {SackChunk::GapAckBlock(3, 4)}; + EXPECT_FALSE( + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab2, false).has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + + EXPECT_CALL(on_discard_, Call(IsUnordered(false), StreamID(1), MID(42))) + .WillOnce(Return(false)); + + // ACK 9, 12, 13, 14 + std::vector gab3 = {SackChunk::GapAckBlock(3, 5)}; + OutstandingData::AckInfo ack = + buf_.HandleSack(unwrapper_.Unwrap(TSN(9)), gab3, false); + EXPECT_TRUE(ack.has_packet_loss); + EXPECT_FALSE(buf_.has_data_to_be_retransmitted()); + EXPECT_THAT(buf_.GetChunksToBeFastRetransmitted(1000), IsEmpty()); + EXPECT_THAT(buf_.GetChunksToBeRetransmitted(1000), IsEmpty()); +} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_queue.cc b/net/dcsctp/tx/retransmission_queue.cc index d980710826..57559195f0 100644 --- a/net/dcsctp/tx/retransmission_queue.cc +++ b/net/dcsctp/tx/retransmission_queue.cc @@ -278,8 +278,12 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { UpdateRTT(now, cumulative_tsn_ack); } + // Exit fast recovery before continuing processing, in case it needs to go + // into fast recovery again due to new reported packet loss. + MaybeExitFastRecovery(cumulative_tsn_ack); + OutstandingData::AckInfo ack_info = outstanding_data_.HandleSack( - cumulative_tsn_ack, sack.gap_ack_blocks(), is_in_fast_retransmit_); + cumulative_tsn_ack, sack.gap_ack_blocks(), is_in_fast_recovery()); // Update of outstanding_data_ is now done. Congestion control remains. UpdateReceiverWindow(sack.a_rwnd()); @@ -292,8 +296,6 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { << old_outstanding_bytes << "), rwnd=" << rwnd_ << " (" << old_rwnd << ")"; - MaybeExitFastRecovery(cumulative_tsn_ack); - if (cumulative_tsn_ack > old_last_cumulative_tsn_ack) { // https://tools.ietf.org/html/rfc4960#section-6.3.2 // "Whenever a SACK is received that acknowledges the DATA chunk @@ -308,7 +310,6 @@ bool RetransmissionQueue::HandleSack(TimeMs now, const SackChunk& sack) { } if (ack_info.has_packet_loss) { - is_in_fast_retransmit_ = true; HandlePacketLoss(ack_info.highest_tsn_acked); } @@ -386,6 +387,43 @@ void RetransmissionQueue::HandleT3RtxTimerExpiry() { RTC_DCHECK(IsConsistent()); } +std::vector> +RetransmissionQueue::GetChunksForFastRetransmit(size_t bytes_in_packet) { + RTC_DCHECK(outstanding_data_.has_data_to_be_fast_retransmitted()); + RTC_DCHECK(IsDivisibleBy4(bytes_in_packet)); + std::vector> to_be_sent; + size_t old_outstanding_bytes = outstanding_bytes(); + + to_be_sent = + outstanding_data_.GetChunksToBeFastRetransmitted(bytes_in_packet); + RTC_DCHECK(!to_be_sent.empty()); + + // https://tools.ietf.org/html/rfc4960#section-6.3.2 + // "Every time a DATA chunk is sent to any address (including a + // retransmission), if the T3-rtx timer of that address is not running, + // start it running so that it will expire after the RTO of that address." + if (!t3_rtx_.is_running()) { + t3_rtx_.Start(); + } + RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Fast-retransmitting TSN " + << StrJoin(to_be_sent, ",", + [&](rtc::StringBuilder& sb, + const std::pair& c) { + sb << *c.first; + }) + << " - " + << absl::c_accumulate( + to_be_sent, 0, + [&](size_t r, const std::pair& d) { + return r + GetSerializedChunkSize(d.second); + }) + << " bytes. outstanding_bytes=" << outstanding_bytes() + << " (" << old_outstanding_bytes << ")"; + + RTC_DCHECK(IsConsistent()); + return to_be_sent; +} + std::vector> RetransmissionQueue::GetChunksToSend( TimeMs now, size_t bytes_remaining_in_packet) { @@ -395,60 +433,42 @@ std::vector> RetransmissionQueue::GetChunksToSend( std::vector> to_be_sent; size_t old_outstanding_bytes = outstanding_bytes(); size_t old_rwnd = rwnd_; - if (is_in_fast_retransmit_) { - // https://tools.ietf.org/html/rfc4960#section-7.2.4 - // "Determine how many of the earliest (i.e., lowest TSN) DATA chunks - // marked for retransmission will fit into a single packet ... Retransmit - // those K DATA chunks in a single packet. When a Fast Retransmit is being - // performed, the sender SHOULD ignore the value of cwnd and SHOULD NOT - // delay retransmission for this single packet." - is_in_fast_retransmit_ = false; - to_be_sent = - outstanding_data_.GetChunksToBeRetransmitted(bytes_remaining_in_packet); - size_t to_be_sent_bytes = absl::c_accumulate( - to_be_sent, 0, [&](size_t r, const std::pair& d) { - return r + GetSerializedChunkSize(d.second); - }); - RTC_DLOG(LS_VERBOSE) << log_prefix_ << "fast-retransmit: sending " - << to_be_sent.size() << " chunks, " << to_be_sent_bytes - << " bytes"; - } else { - // Normal sending. Calculate the bandwidth budget (how many bytes that is - // allowed to be sent), and fill that up first with chunks that are - // scheduled to be retransmitted. If there is still budget, send new chunks - // (which will have their TSN assigned here.) - size_t max_bytes = - RoundDownTo4(std::min(max_bytes_to_send(), bytes_remaining_in_packet)); - - to_be_sent = outstanding_data_.GetChunksToBeRetransmitted(max_bytes); - max_bytes -= absl::c_accumulate( - to_be_sent, 0, [&](size_t r, const std::pair& d) { - return r + GetSerializedChunkSize(d.second); - }); - - while (max_bytes > data_chunk_header_size_) { - RTC_DCHECK(IsDivisibleBy4(max_bytes)); - absl::optional chunk_opt = - send_queue_.Produce(now, max_bytes - data_chunk_header_size_); - if (!chunk_opt.has_value()) { - break; - } - - size_t chunk_size = GetSerializedChunkSize(chunk_opt->data); - max_bytes -= chunk_size; - rwnd_ -= chunk_size; - - absl::optional tsn = outstanding_data_.Insert( - chunk_opt->data, - partial_reliability_ ? chunk_opt->max_retransmissions - : MaxRetransmits::NoLimit(), - now, - partial_reliability_ ? chunk_opt->expires_at - : TimeMs::InfiniteFuture()); - - if (tsn.has_value()) { - to_be_sent.emplace_back(tsn->Wrap(), std::move(chunk_opt->data)); - } + + // Calculate the bandwidth budget (how many bytes that is + // allowed to be sent), and fill that up first with chunks that are + // scheduled to be retransmitted. If there is still budget, send new chunks + // (which will have their TSN assigned here.) + size_t max_bytes = + RoundDownTo4(std::min(max_bytes_to_send(), bytes_remaining_in_packet)); + + to_be_sent = outstanding_data_.GetChunksToBeRetransmitted(max_bytes); + max_bytes -= absl::c_accumulate(to_be_sent, 0, + [&](size_t r, const std::pair& d) { + return r + GetSerializedChunkSize(d.second); + }); + + while (max_bytes > data_chunk_header_size_) { + RTC_DCHECK(IsDivisibleBy4(max_bytes)); + absl::optional chunk_opt = + send_queue_.Produce(now, max_bytes - data_chunk_header_size_); + if (!chunk_opt.has_value()) { + break; + } + + size_t chunk_size = GetSerializedChunkSize(chunk_opt->data); + max_bytes -= chunk_size; + rwnd_ -= chunk_size; + + absl::optional tsn = outstanding_data_.Insert( + chunk_opt->data, + partial_reliability_ ? chunk_opt->max_retransmissions + : MaxRetransmits::NoLimit(), + now, + partial_reliability_ ? chunk_opt->expires_at + : TimeMs::InfiniteFuture()); + + if (tsn.has_value()) { + to_be_sent.emplace_back(tsn->Wrap(), std::move(chunk_opt->data)); } } @@ -509,16 +529,15 @@ size_t RetransmissionQueue::max_bytes_to_send() const { return std::min(rwnd(), left); } -void RetransmissionQueue::PrepareResetStreams( - rtc::ArrayView streams) { +void RetransmissionQueue::PrepareResetStream(StreamID stream_id) { // TODO(boivie): These calls are now only affecting the send queue. The // packet buffer can also change behavior - for example draining the chunk // producer and eagerly assign TSNs so that an "Outgoing SSN Reset Request" // can be sent quickly, with a known `sender_last_assigned_tsn`. - send_queue_.PrepareResetStreams(streams); + send_queue_.PrepareResetStream(stream_id); } -bool RetransmissionQueue::CanResetStreams() const { - return send_queue_.CanResetStreams(); +bool RetransmissionQueue::HasStreamsReadyToBeReset() const { + return send_queue_.HasStreamsReadyToBeReset(); } void RetransmissionQueue::CommitResetStreams() { send_queue_.CommitResetStreams(); diff --git a/net/dcsctp/tx/retransmission_queue.h b/net/dcsctp/tx/retransmission_queue.h index 08f11db744..1958dfd643 100644 --- a/net/dcsctp/tx/retransmission_queue.h +++ b/net/dcsctp/tx/retransmission_queue.h @@ -74,6 +74,16 @@ class RetransmissionQueue { // Handles an expired retransmission timer. void HandleT3RtxTimerExpiry(); + bool has_data_to_be_fast_retransmitted() const { + return outstanding_data_.has_data_to_be_fast_retransmitted(); + } + + // Returns a list of chunks to "fast retransmit" that would fit in one SCTP + // packet with `bytes_in_packet` bytes available. The current value + // of `cwnd` is ignored. + std::vector> GetChunksForFastRetransmit( + size_t bytes_in_packet); + // Returns a list of chunks to send that would fit in one SCTP packet with // `bytes_remaining_in_packet` bytes available. This may be further limited by // the congestion control windows. Note that `ShouldSendForwardTSN` must be @@ -133,8 +143,11 @@ class RetransmissionQueue { // See the SendQueue for a longer description of these methods related // to stream resetting. - void PrepareResetStreams(rtc::ArrayView streams); - bool CanResetStreams() const; + void PrepareResetStream(StreamID stream_id); + bool HasStreamsReadyToBeReset() const; + std::vector GetStreamsReadyToBeReset() const { + return send_queue_.GetStreamsReadyToBeReset(); + } void CommitResetStreams(); void RollbackResetStreams(); @@ -229,8 +242,6 @@ class RetransmissionQueue { // If set, fast recovery is enabled until this TSN has been cumulative // acked. absl::optional fast_recovery_exit_tsn_ = absl::nullopt; - // Indicates if the congestion algorithm is in fast retransmit. - bool is_in_fast_retransmit_ = false; // The send queue. SendQueue& send_queue_; diff --git a/net/dcsctp/tx/retransmission_queue_test.cc b/net/dcsctp/tx/retransmission_queue_test.cc index 8294a3111d..b232bb2d82 100644 --- a/net/dcsctp/tx/retransmission_queue_test.cc +++ b/net/dcsctp/tx/retransmission_queue_test.cc @@ -78,6 +78,14 @@ class RetransmissionQueueTest : public testing::Test { }; } + std::vector GetTSNsForFastRetransmit(RetransmissionQueue& queue) { + std::vector tsns; + for (const auto& elem : queue.GetChunksForFastRetransmit(10000)) { + tsns.push_back(elem.first); + } + return tsns; + } + std::vector GetSentPacketTSNs(RetransmissionQueue& queue) { std::vector tsns; for (const auto& elem : queue.GetChunksToSend(now_, 10000)) { @@ -279,7 +287,8 @@ TEST_F(RetransmissionQueueTest, ResendPacketsWhenNackedThreeTimes) { // resent right now. The send queue will not even be queried. EXPECT_CALL(producer_, Produce).Times(0); - EXPECT_THAT(GetSentPacketTSNs(queue), testing::ElementsAre(TSN(13), TSN(16))); + EXPECT_THAT(GetTSNsForFastRetransmit(queue), + testing::ElementsAre(TSN(13), TSN(16))); EXPECT_THAT(queue.GetChunkStatesForTesting(), ElementsAre(Pair(TSN(12), State::kAcked), // @@ -1140,7 +1149,8 @@ TEST_F(RetransmissionQueueTest, AbandonsRtxLimit2WhenNackedNineTimes) { Pair(TSN(18), State::kInFlight), // Pair(TSN(19), State::kInFlight))); - EXPECT_THAT(queue.GetChunksToSend(now_, 1000), ElementsAre(Pair(TSN(10), _))); + EXPECT_THAT(queue.GetChunksForFastRetransmit(1000), + ElementsAre(Pair(TSN(10), _))); // Ack TSN [14 to 16] - three more nacks - second and last retransmission. for (int tsn = 14; tsn <= 16; ++tsn) { @@ -1375,7 +1385,7 @@ TEST_F(RetransmissionQueueTest, ReadyForHandoverWhenNothingToRetransmit) { // Send "fast retransmit" mode chunks EXPECT_CALL(producer_, Produce).Times(0); - EXPECT_THAT(GetSentPacketTSNs(queue), SizeIs(2)); + EXPECT_THAT(GetTSNsForFastRetransmit(queue), SizeIs(2)); EXPECT_EQ( queue.GetHandoverReadiness(), HandoverReadinessStatus() diff --git a/net/dcsctp/tx/rr_send_queue.cc b/net/dcsctp/tx/rr_send_queue.cc index a20ccb2448..d4ce59d58c 100644 --- a/net/dcsctp/tx/rr_send_queue.cc +++ b/net/dcsctp/tx/rr_send_queue.cc @@ -32,8 +32,7 @@ RRSendQueue::RRSendQueue(absl::string_view log_prefix, size_t buffer_size, std::function on_buffered_amount_low, size_t total_buffered_amount_low_threshold, - std::function on_total_buffered_amount_low, - const DcSctpSocketHandoverState* handover_state) + std::function on_total_buffered_amount_low) : log_prefix_(std::string(log_prefix) + "fcfs: "), buffer_size_(buffer_size), on_buffered_amount_low_(std::move(on_buffered_amount_low)), @@ -42,10 +41,18 @@ RRSendQueue::RRSendQueue(absl::string_view log_prefix, } bool RRSendQueue::OutgoingStream::HasDataToSend(TimeMs now) { + if (pause_state_ == PauseState::kPaused || + pause_state_ == PauseState::kResetting) { + // The stream has paused (and there is no partially sent message). + return false; + } + while (!items_.empty()) { RRSendQueue::OutgoingStream::Item& item = items_.front(); if (item.message_id.has_value()) { - // Already partially sent messages can always continue to be sent. + // Already partially sent messages can always continue to be sent. This + // ensures e.g. that paused streams with partially sent messages get to + // send the partial message in full before resetting. return true; } @@ -58,10 +65,6 @@ bool RRSendQueue::OutgoingStream::HasDataToSend(TimeMs now) { continue; } - if (is_paused_) { - // The stream has paused (and there is no partially sent message). - return false; - } return true; } return false; @@ -135,19 +138,15 @@ void RRSendQueue::OutgoingStream::Add(DcSctpMessage message, RTC_DCHECK(IsConsistent()); } -absl::optional RRSendQueue::OutgoingStream::Produce( - TimeMs now, - size_t max_size) { +SendQueue::DataToSend RRSendQueue::OutgoingStream::Produce(TimeMs now, + size_t max_size) { RTC_DCHECK(!items_.empty()); + RTC_DCHECK(pause_state_ != PauseState::kPaused && + pause_state_ != PauseState::kResetting); Item* item = &items_.front(); DcSctpMessage& message = item->message; - if (item->remaining_size > max_size && max_size < kMinimumFragmentedPayload) { - RTC_DCHECK(IsConsistent()); - return absl::nullopt; - } - // Allocate Message ID and SSN when the first fragment is sent. if (!item->message_id.has_value()) { MID& mid = @@ -201,6 +200,12 @@ absl::optional RRSendQueue::OutgoingStream::Produce( // The entire message has been sent, and its last data copied to `chunk`, so // it can safely be discarded. items_.pop_front(); + + if (pause_state_ == PauseState::kPending) { + RTC_DLOG(LS_VERBOSE) << "Pause state on " << *stream_id + << " is moving from pending to paused"; + pause_state_ = PauseState::kPaused; + } } else { item->remaining_offset += chunk_payload.size(); item->remaining_size -= chunk_payload.size(); @@ -222,6 +227,11 @@ bool RRSendQueue::OutgoingStream::Discard(IsUnordered unordered, buffered_amount_.Decrease(item.remaining_size); total_buffered_amount_.Decrease(item.remaining_size); items_.pop_front(); + + if (pause_state_ == PauseState::kPending) { + pause_state_ = PauseState::kPaused; + } + // As the item still existed, it had unsent data. result = true; } @@ -231,14 +241,26 @@ bool RRSendQueue::OutgoingStream::Discard(IsUnordered unordered, } void RRSendQueue::OutgoingStream::Pause() { - is_paused_ = true; + if (pause_state_ != PauseState::kNotPaused) { + // Already in progress. + return; + } + + bool had_pending_items = !items_.empty(); + + // https://datatracker.ietf.org/doc/html/rfc8831#section-6.7 + // "Closing of a data channel MUST be signaled by resetting the corresponding + // outgoing streams [RFC6525]. This means that if one side decides to close + // the data channel, it resets the corresponding outgoing stream." + // ... "[RFC6525] also guarantees that all the messages are delivered (or + // abandoned) before the stream is reset." // A stream is paused when it's about to be reset. In this implementation, - // it will throw away all non-partially send messages. This is subject to - // change. It will however not discard any partially sent messages - only - // whole messages. Partially delivered messages (at the time of receiving a - // Stream Reset command) will always deliver all the fragments before - // actually resetting the stream. + // it will throw away all non-partially send messages - they will be abandoned + // as noted above. This is subject to change. It will however not discard any + // partially sent messages - only whole messages. Partially delivered messages + // (at the time of receiving a Stream Reset command) will always deliver all + // the fragments before actually resetting the stream. for (auto it = items_.begin(); it != items_.end();) { if (it->remaining_offset == 0) { buffered_amount_.Decrease(it->remaining_size); @@ -248,10 +270,33 @@ void RRSendQueue::OutgoingStream::Pause() { ++it; } } + + pause_state_ = (items_.empty() || items_.front().remaining_offset == 0) + ? PauseState::kPaused + : PauseState::kPending; + + if (had_pending_items && pause_state_ == PauseState::kPaused) { + RTC_DLOG(LS_VERBOSE) << "Stream " << *stream_id() + << " was previously active, but is now paused."; + } + + RTC_DCHECK(IsConsistent()); +} + +void RRSendQueue::OutgoingStream::Resume() { + RTC_DCHECK(pause_state_ == PauseState::kResetting); + if (!items_.empty()) { + RTC_DLOG(LS_VERBOSE) << "Stream " << *stream_id() + << " was previously paused, but is now active."; + } + pause_state_ = PauseState::kNotPaused; RTC_DCHECK(IsConsistent()); } void RRSendQueue::OutgoingStream::Reset() { + // This can be called both when an outgoing stream reset has been responded + // to, or when the entire SendQueue is reset due to detecting the peer having + // restarted. The stream may be in any state at this time. if (!items_.empty()) { // If this message has been partially sent, reset it so that it will be // re-sent. @@ -266,7 +311,7 @@ void RRSendQueue::OutgoingStream::Reset() { item.ssn = absl::nullopt; item.current_fsn = FSN(0); } - is_paused_ = false; + pause_state_ = PauseState::kNotPaused; next_ordered_mid_ = MID(0); next_unordered_mid_ = MID(0); next_ssn_ = SSN(0); @@ -346,22 +391,20 @@ absl::optional RRSendQueue::Produce(TimeMs now, RTC_DCHECK(stream_it != streams_.end()); } - absl::optional data = stream_it->second.Produce(now, max_size); - if (data.has_value()) { - RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Producing DATA, type=" - << (data->data.is_unordered ? "unordered" : "ordered") - << "::" - << (*data->data.is_beginning && *data->data.is_end - ? "complete" - : *data->data.is_beginning - ? "first" - : *data->data.is_end ? "last" : "middle") - << ", stream_id=" << *stream_it->first - << ", ppid=" << *data->data.ppid - << ", length=" << data->data.payload.size(); + DataToSend data = stream_it->second.Produce(now, max_size); + RTC_DLOG(LS_VERBOSE) << log_prefix_ << "Producing DATA, type=" + << (data.data.is_unordered ? "unordered" : "ordered") + << "::" + << (*data.data.is_beginning && *data.data.is_end + ? "complete" + : *data.data.is_beginning + ? "first" + : *data.data.is_end ? "last" : "middle") + << ", stream_id=" << *stream_it->first + << ", ppid=" << *data.data.ppid + << ", length=" << data.data.payload.size(); - previous_message_has_ended_ = *data->data.is_end; - } + previous_message_has_ended_ = *data.data.is_end; RTC_DCHECK(IsConsistent()); return data; @@ -381,27 +424,39 @@ bool RRSendQueue::Discard(IsUnordered unordered, return has_discarded; } -void RRSendQueue::PrepareResetStreams(rtc::ArrayView streams) { - for (StreamID stream_id : streams) { - GetOrCreateStreamInfo(stream_id).Pause(); - } +void RRSendQueue::PrepareResetStream(StreamID stream_id) { + GetOrCreateStreamInfo(stream_id).Pause(); RTC_DCHECK(IsConsistent()); } -bool RRSendQueue::CanResetStreams() const { - // Streams can be reset if those streams that are paused don't have any - // messages that are partially sent. +bool RRSendQueue::HasStreamsReadyToBeReset() const { for (auto& [unused, stream] : streams_) { - if (stream.is_paused() && stream.has_partially_sent_message()) { - return false; + if (stream.IsReadyToBeReset()) { + return true; + } + } + return false; +} +std::vector RRSendQueue::GetStreamsReadyToBeReset() { + RTC_DCHECK(absl::c_count_if(streams_, [](const auto& p) { + return p.second.IsResetting(); + }) == 0); + std::vector ready; + for (auto& [stream_id, stream] : streams_) { + if (stream.IsReadyToBeReset()) { + stream.SetAsResetting(); + ready.push_back(stream_id); } } - return true; + return ready; } void RRSendQueue::CommitResetStreams() { + RTC_DCHECK(absl::c_count_if(streams_, [](const auto& p) { + return p.second.IsResetting(); + }) > 0); for (auto& [unused, stream] : streams_) { - if (stream.is_paused()) { + if (stream.IsResetting()) { stream.Reset(); } } @@ -409,8 +464,13 @@ void RRSendQueue::CommitResetStreams() { } void RRSendQueue::RollbackResetStreams() { + RTC_DCHECK(absl::c_count_if(streams_, [](const auto& p) { + return p.second.IsResetting(); + }) > 0); for (auto& [unused, stream] : streams_) { - stream.Resume(); + if (stream.IsResetting()) { + stream.Resume(); + } } RTC_DCHECK(IsConsistent()); } @@ -455,6 +515,7 @@ RRSendQueue::OutgoingStream& RRSendQueue::GetOrCreateStreamInfo( return streams_ .emplace(stream_id, OutgoingStream( + stream_id, [this, stream_id]() { on_buffered_amount_low_(stream_id); }, total_buffered_amount_)) .first->second; @@ -482,6 +543,7 @@ void RRSendQueue::RestoreFromState(const DcSctpSocketHandoverState& state) { state.tx.streams) { StreamID stream_id(state_stream.id); streams_.emplace(stream_id, OutgoingStream( + stream_id, [this, stream_id]() { on_buffered_amount_low_(stream_id); }, diff --git a/net/dcsctp/tx/rr_send_queue.h b/net/dcsctp/tx/rr_send_queue.h index 3007cd0fe8..57a43ccd66 100644 --- a/net/dcsctp/tx/rr_send_queue.h +++ b/net/dcsctp/tx/rr_send_queue.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "absl/algorithm/container.h" #include "absl/strings/string_view.h" @@ -40,15 +41,11 @@ namespace dcsctp { // established, this send queue is always present - even for closed connections. class RRSendQueue : public SendQueue { public: - // How small a data chunk's payload may be, if having to fragment a message. - static constexpr size_t kMinimumFragmentedPayload = 10; - RRSendQueue(absl::string_view log_prefix, size_t buffer_size, std::function on_buffered_amount_low, size_t total_buffered_amount_low_threshold, - std::function on_total_buffered_amount_low, - const DcSctpSocketHandoverState* handover_state = nullptr); + std::function on_total_buffered_amount_low); // Indicates if the buffer is full. Note that it's up to the caller to ensure // that the buffer is not full prior to adding new items to it. @@ -69,8 +66,9 @@ class RRSendQueue : public SendQueue { bool Discard(IsUnordered unordered, StreamID stream_id, MID message_id) override; - void PrepareResetStreams(rtc::ArrayView streams) override; - bool CanResetStreams() const override; + void PrepareResetStream(StreamID streams) override; + bool HasStreamsReadyToBeReset() const override; + std::vector GetStreamsReadyToBeReset() override; void CommitResetStreams() override; void RollbackResetStreams() override; void Reset() override; @@ -112,23 +110,28 @@ class RRSendQueue : public SendQueue { // Per-stream information. class OutgoingStream { public: - explicit OutgoingStream( + OutgoingStream( + StreamID stream_id, std::function on_buffered_amount_low, ThresholdWatcher& total_buffered_amount, const DcSctpSocketHandoverState::OutgoingStream* state = nullptr) - : next_unordered_mid_(MID(state ? state->next_unordered_mid : 0)), + : stream_id_(stream_id), + next_unordered_mid_(MID(state ? state->next_unordered_mid : 0)), next_ordered_mid_(MID(state ? state->next_ordered_mid : 0)), next_ssn_(SSN(state ? state->next_ssn : 0)), buffered_amount_(std::move(on_buffered_amount_low)), total_buffered_amount_(total_buffered_amount) {} + StreamID stream_id() const { return stream_id_; } + // Enqueues a message to this stream. void Add(DcSctpMessage message, TimeMs expires_at, const SendOptions& send_options); - // Possibly produces a data chunk to send. - absl::optional Produce(TimeMs now, size_t max_size); + // Produces a data chunk to send. This is only called on streams that have + // data available. + DataToSend Produce(TimeMs now, size_t max_size); const ThresholdWatcher& buffered_amount() const { return buffered_amount_; } ThresholdWatcher& buffered_amount() { return buffered_amount_; } @@ -140,9 +143,18 @@ class RRSendQueue : public SendQueue { void Pause(); // Resumes a paused stream. - void Resume() { is_paused_ = false; } + void Resume(); + + bool IsReadyToBeReset() const { + return pause_state_ == PauseState::kPaused; + } - bool is_paused() const { return is_paused_; } + bool IsResetting() const { return pause_state_ == PauseState::kResetting; } + + void SetAsResetting() { + RTC_DCHECK(pause_state_ == PauseState::kPaused); + pause_state_ = PauseState::kResetting; + } // Resets this stream, meaning MIDs and SSNs are set to zero. void Reset(); @@ -158,6 +170,26 @@ class RRSendQueue : public SendQueue { DcSctpSocketHandoverState::OutgoingStream& state) const; private: + // Streams are paused before they can be reset. To reset a stream, the + // socket sends an outgoing stream reset command with the TSN of the last + // fragment of the last message, so that receivers and senders can agree on + // when it stopped. And if the send queue is in the middle of sending a + // message, and without fragments not yet sent and without TSNs allocated to + // them, it will keep sending data until that message has ended. + enum class PauseState { + // The stream is not paused, and not scheduled to be reset. + kNotPaused, + // The stream has requested to be reset/paused but is still producing + // fragments of a message that hasn't ended yet. When it does, it will + // transition to the `kPaused` state. + kPending, + // The stream is fully paused and can be reset. + kPaused, + // The stream has been added to an outgoing stream reset request and a + // response from the peer hasn't been received yet. + kResetting, + }; + // An enqueued message and metadata. struct Item { explicit Item(DcSctpMessage msg, @@ -185,8 +217,8 @@ class RRSendQueue : public SendQueue { bool IsConsistent() const; - // Streams are pause when they are about to be reset. - bool is_paused_ = false; + const StreamID stream_id_; + PauseState pause_state_ = PauseState::kNotPaused; // MIDs are different for unordered and ordered messages sent on a stream. MID next_unordered_mid_; MID next_ordered_mid_; diff --git a/net/dcsctp/tx/rr_send_queue_test.cc b/net/dcsctp/tx/rr_send_queue_test.cc index 425027762d..fbbce58de1 100644 --- a/net/dcsctp/tx/rr_send_queue_test.cc +++ b/net/dcsctp/tx/rr_send_queue_test.cc @@ -26,6 +26,7 @@ namespace dcsctp { namespace { using ::testing::SizeIs; +using ::testing::UnorderedElementsAre; constexpr TimeMs kNow = TimeMs(0); constexpr StreamID kStreamID(1); @@ -154,35 +155,6 @@ TEST_F(RRSendQueueTest, BufferBecomesFullAndEmptied) { EXPECT_TRUE(buf_.IsEmpty()); } -TEST_F(RRSendQueueTest, WillNotSendTooSmallPacket) { - std::vector payload(RRSendQueue::kMinimumFragmentedPayload + 1); - buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); - - // Wouldn't fit enough payload (wouldn't want to fragment) - EXPECT_FALSE( - buf_.Produce(kNow, - /*max_size=*/RRSendQueue::kMinimumFragmentedPayload - 1) - .has_value()); - - // Minimum fragment - absl::optional chunk_one = - buf_.Produce(kNow, - /*max_size=*/RRSendQueue::kMinimumFragmentedPayload); - ASSERT_TRUE(chunk_one.has_value()); - EXPECT_EQ(chunk_one->data.stream_id, kStreamID); - EXPECT_EQ(chunk_one->data.ppid, kPPID); - - // There is only one byte remaining - it can be fetched as it doesn't require - // additional fragmentation. - absl::optional chunk_two = - buf_.Produce(kNow, /*max_size=*/1); - ASSERT_TRUE(chunk_two.has_value()); - EXPECT_EQ(chunk_two->data.stream_id, kStreamID); - EXPECT_EQ(chunk_two->data.ppid, kPPID); - - EXPECT_TRUE(buf_.IsEmpty()); -} - TEST_F(RRSendQueueTest, DefaultsToOrderedSend) { std::vector payload(20); @@ -281,10 +253,13 @@ TEST_F(RRSendQueueTest, PrepareResetStreamsDiscardsStream) { buf_.Add(kNow, DcSctpMessage(StreamID(2), PPID(54), {1, 2, 3, 4, 5})); EXPECT_EQ(buf_.total_buffered_amount(), 8u); - buf_.PrepareResetStreams(std::vector({StreamID(1)})); + buf_.PrepareResetStream(StreamID(1)); EXPECT_EQ(buf_.total_buffered_amount(), 5u); + + EXPECT_THAT(buf_.GetStreamsReadyToBeReset(), + UnorderedElementsAre(StreamID(1))); buf_.CommitResetStreams(); - buf_.PrepareResetStreams(std::vector({StreamID(2)})); + buf_.PrepareResetStream(StreamID(2)); EXPECT_EQ(buf_.total_buffered_amount(), 0u); } @@ -299,21 +274,27 @@ TEST_F(RRSendQueueTest, PrepareResetStreamsNotPartialPackets) { EXPECT_EQ(chunk_one->data.stream_id, kStreamID); EXPECT_EQ(buf_.total_buffered_amount(), 2 * payload.size() - 50); - StreamID stream_ids[] = {StreamID(1)}; - buf_.PrepareResetStreams(stream_ids); + buf_.PrepareResetStream(StreamID(1)); EXPECT_EQ(buf_.total_buffered_amount(), payload.size() - 50); } TEST_F(RRSendQueueTest, EnqueuedItemsArePausedDuringStreamReset) { std::vector payload(50); - buf_.PrepareResetStreams(std::vector({StreamID(1)})); + buf_.PrepareResetStream(StreamID(1)); EXPECT_EQ(buf_.total_buffered_amount(), 0u); buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); EXPECT_EQ(buf_.total_buffered_amount(), payload.size()); EXPECT_FALSE(buf_.Produce(kNow, kOneFragmentPacketSize).has_value()); + + EXPECT_TRUE(buf_.HasStreamsReadyToBeReset()); + EXPECT_THAT(buf_.GetStreamsReadyToBeReset(), + UnorderedElementsAre(StreamID(1))); + + EXPECT_FALSE(buf_.Produce(kNow, kOneFragmentPacketSize).has_value()); + buf_.CommitResetStreams(); EXPECT_EQ(buf_.total_buffered_amount(), payload.size()); @@ -323,6 +304,35 @@ TEST_F(RRSendQueueTest, EnqueuedItemsArePausedDuringStreamReset) { EXPECT_EQ(buf_.total_buffered_amount(), 0u); } +TEST_F(RRSendQueueTest, PausedStreamsStillSendPartialMessagesUntilEnd) { + constexpr size_t kPayloadSize = 100; + constexpr size_t kFragmentSize = 50; + std::vector payload(kPayloadSize); + + buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); + buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); + + absl::optional chunk_one = + buf_.Produce(kNow, kFragmentSize); + ASSERT_TRUE(chunk_one.has_value()); + EXPECT_EQ(chunk_one->data.stream_id, kStreamID); + EXPECT_EQ(buf_.total_buffered_amount(), 2 * kPayloadSize - kFragmentSize); + + // This will stop the second message from being sent. + buf_.PrepareResetStream(StreamID(1)); + EXPECT_EQ(buf_.total_buffered_amount(), 1 * kPayloadSize - kFragmentSize); + + // Should still produce fragments until end of message. + absl::optional chunk_two = + buf_.Produce(kNow, kFragmentSize); + ASSERT_TRUE(chunk_two.has_value()); + EXPECT_EQ(chunk_two->data.stream_id, kStreamID); + EXPECT_EQ(buf_.total_buffered_amount(), 0ul); + + // But shouldn't produce any more messages as the stream is paused. + EXPECT_FALSE(buf_.Produce(kNow, kFragmentSize).has_value()); +} + TEST_F(RRSendQueueTest, CommittingResetsSSN) { std::vector payload(50); @@ -339,13 +349,14 @@ TEST_F(RRSendQueueTest, CommittingResetsSSN) { ASSERT_TRUE(chunk_two.has_value()); EXPECT_EQ(chunk_two->data.ssn, SSN(1)); - StreamID stream_ids[] = {StreamID(1)}; - buf_.PrepareResetStreams(stream_ids); + buf_.PrepareResetStream(StreamID(1)); // Buffered buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); - EXPECT_TRUE(buf_.CanResetStreams()); + EXPECT_TRUE(buf_.HasStreamsReadyToBeReset()); + EXPECT_THAT(buf_.GetStreamsReadyToBeReset(), + UnorderedElementsAre(StreamID(1))); buf_.CommitResetStreams(); absl::optional chunk_three = @@ -372,14 +383,16 @@ TEST_F(RRSendQueueTest, CommittingResetsSSNForPausedStreamsOnly) { EXPECT_EQ(chunk_two->data.stream_id, StreamID(3)); EXPECT_EQ(chunk_two->data.ssn, SSN(0)); - StreamID stream_ids[] = {StreamID(3)}; - buf_.PrepareResetStreams(stream_ids); + buf_.PrepareResetStream(StreamID(3)); // Send two more messages - SID 3 will buffer, SID 1 will send. buf_.Add(kNow, DcSctpMessage(StreamID(1), kPPID, payload)); buf_.Add(kNow, DcSctpMessage(StreamID(3), kPPID, payload)); - EXPECT_TRUE(buf_.CanResetStreams()); + EXPECT_TRUE(buf_.HasStreamsReadyToBeReset()); + EXPECT_THAT(buf_.GetStreamsReadyToBeReset(), + UnorderedElementsAre(StreamID(3))); + buf_.CommitResetStreams(); absl::optional chunk_three = @@ -411,12 +424,14 @@ TEST_F(RRSendQueueTest, RollBackResumesSSN) { ASSERT_TRUE(chunk_two.has_value()); EXPECT_EQ(chunk_two->data.ssn, SSN(1)); - buf_.PrepareResetStreams(std::vector({StreamID(1)})); + buf_.PrepareResetStream(StreamID(1)); // Buffered buf_.Add(kNow, DcSctpMessage(kStreamID, kPPID, payload)); - EXPECT_TRUE(buf_.CanResetStreams()); + EXPECT_TRUE(buf_.HasStreamsReadyToBeReset()); + EXPECT_THAT(buf_.GetStreamsReadyToBeReset(), + UnorderedElementsAre(StreamID(1))); buf_.RollbackResetStreams(); absl::optional chunk_three = @@ -744,40 +759,5 @@ TEST_F(RRSendQueueTest, WillStayInAStreamAsLongAsThatMessageIsSending) { EXPECT_FALSE(buf_.Produce(kNow, kOneFragmentPacketSize).has_value()); } - -TEST_F(RRSendQueueTest, WillStayInStreamWhenOnlySmallFragmentRemaining) { - buf_.Add(kNow, - DcSctpMessage(StreamID(5), kPPID, - std::vector(kOneFragmentPacketSize * 2))); - buf_.Add(kNow, DcSctpMessage(StreamID(6), kPPID, std::vector(1))); - - ASSERT_HAS_VALUE_AND_ASSIGN(SendQueue::DataToSend chunk1, - buf_.Produce(kNow, kOneFragmentPacketSize)); - EXPECT_EQ(chunk1.data.stream_id, StreamID(5)); - EXPECT_THAT(chunk1.data.payload, SizeIs(kOneFragmentPacketSize)); - - // Now assume that there will be a lot of previous chunks that need to be - // retransmitted, which fills up the next packet and there is little space - // left in the packet for new chunks. What it should NOT do right now is to - // try to send a message from StreamID 6. And it should not try to send a very - // small fragment from StreamID 5 either. So just skip this one. - EXPECT_FALSE(buf_.Produce(kNow, 8).has_value()); - - // When the next produce request comes with a large buffer to fill, continue - // sending from StreamID 5. - - ASSERT_HAS_VALUE_AND_ASSIGN(SendQueue::DataToSend chunk2, - buf_.Produce(kNow, kOneFragmentPacketSize)); - EXPECT_EQ(chunk2.data.stream_id, StreamID(5)); - EXPECT_THAT(chunk2.data.payload, SizeIs(kOneFragmentPacketSize)); - - // Lastly, produce a message on StreamID 6. - ASSERT_HAS_VALUE_AND_ASSIGN(SendQueue::DataToSend chunk3, - buf_.Produce(kNow, kOneFragmentPacketSize)); - EXPECT_EQ(chunk3.data.stream_id, StreamID(6)); - EXPECT_THAT(chunk3.data.payload, SizeIs(1)); - - EXPECT_FALSE(buf_.Produce(kNow, 8).has_value()); -} } // namespace } // namespace dcsctp diff --git a/net/dcsctp/tx/send_queue.h b/net/dcsctp/tx/send_queue.h index a821d20785..b2e5a9d436 100644 --- a/net/dcsctp/tx/send_queue.h +++ b/net/dcsctp/tx/send_queue.h @@ -67,11 +67,11 @@ class SendQueue { StreamID stream_id, MID message_id) = 0; - // Prepares the streams to be reset. This is used to close a WebRTC data + // Prepares the stream to be reset. This is used to close a WebRTC data // channel and will be signaled to the other side. // // Concretely, it discards all whole (not partly sent) messages in the given - // streams and pauses those streams so that future added messages aren't + // stream and pauses that stream so that future added messages aren't // produced until `ResumeStreams` is called. // // TODO(boivie): Investigate if it really should discard any message at all. @@ -82,24 +82,28 @@ class SendQueue { // reset, and paused while they are resetting. This is the first part of the // two-phase commit protocol to reset streams, where the caller completes the // procedure by either calling `CommitResetStreams` or `RollbackResetStreams`. - virtual void PrepareResetStreams(rtc::ArrayView streams) = 0; + virtual void PrepareResetStream(StreamID stream_id) = 0; - // Returns true if all non-discarded messages during `PrepareResetStreams` - // (which are those that was partially sent before that method was called) - // have been sent. - virtual bool CanResetStreams() const = 0; + // Indicates if there are any streams that are ready to be reset. + virtual bool HasStreamsReadyToBeReset() const = 0; - // Called to commit to reset the streams provided to `PrepareResetStreams`. - // It will reset the stream sequence numbers (SSNs) and message identifiers - // (MIDs) and resume the paused streams. + // Returns a list of streams that are ready to be included in an outgoing + // stream reset request. Any streams that are returned here must be included + // in an outgoing stream reset request, and there must not be concurrent + // requests. Before calling this method again, you must have called + virtual std::vector GetStreamsReadyToBeReset() = 0; + + // Called to commit to reset the streams returned by + // `GetStreamsReadyToBeReset`. It will reset the stream sequence numbers + // (SSNs) and message identifiers (MIDs) and resume the paused streams. virtual void CommitResetStreams() = 0; - // Called to abort the resetting of streams provided to `PrepareResetStreams`. - // Will resume the paused streams without resetting the stream sequence - // numbers (SSNs) or message identifiers (MIDs). Note that the non-partial - // messages that were discarded when calling `PrepareResetStreams` will not be - // recovered, to better match the intention from the sender to "close the - // channel". + // Called to abort the resetting of streams returned by + // `GetStreamsReadyToBeReset`. Will resume the paused streams without + // resetting the stream sequence numbers (SSNs) or message identifiers (MIDs). + // Note that the non-partial messages that were discarded when calling + // `PrepareResetStreams` will not be recovered, to better match the intention + // from the sender to "close the channel". virtual void RollbackResetStreams() = 0; // Resets all message identifier counters (MID, SSN) and makes all partially diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index a9c059bed3..3fdf607bf3 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -9,7 +9,7 @@ import("../webrtc.gni") group("p2p") { - public_deps = [ + deps = [ ":libstunprober", ":rtc_p2p", ] @@ -86,6 +86,7 @@ rtc_library("rtc_p2p") { deps = [ "../api:array_view", "../api:async_dns_resolver", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:packet_socket_factory", "../api:rtc_error", @@ -96,23 +97,37 @@ rtc_library("rtc_p2p") { "../api/rtc_event_log", "../api/task_queue", "../api/transport:enums", + "../api/transport:field_trial_based_config", "../api/transport:stun_types", "../logging:ice_log", "../rtc_base", "../rtc_base:async_resolver_interface", + "../rtc_base:buffer", + "../rtc_base:buffer_queue", + "../rtc_base:byte_buffer", + "../rtc_base:byte_order", "../rtc_base:callback_list", "../rtc_base:checks", + "../rtc_base:event_tracer", "../rtc_base:ip_address", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:net_helpers", "../rtc_base:network_constants", + "../rtc_base:rate_tracker", + "../rtc_base:refcount", "../rtc_base:rtc_numerics", "../rtc_base:socket", "../rtc_base:socket_address", "../rtc_base:socket_factory", "../rtc_base:socket_server", + "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/containers:flat_map", "../rtc_base/experiments:field_trial_parser", + "../rtc_base/memory:always_valid_pointer", "../rtc_base/system:no_unique_address", # Needed by pseudo_tcp, which should move to a separate target. @@ -125,7 +140,6 @@ rtc_library("rtc_p2p") { "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", - "../system_wrappers:field_trial", "../system_wrappers:metrics", ] absl_deps = [ @@ -145,7 +159,7 @@ if (rtc_include_tests) { ":rtc_p2p", "../api:libjingle_peerconnection_api", "../rtc_base", - "../rtc_base:rtc_base_approved", + "../rtc_base:copy_on_write_buffer", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", ] @@ -164,6 +178,7 @@ if (rtc_include_tests) { "../rtc_base", "../rtc_base:net_helpers", "../rtc_base:threading", + "../test:scoped_key_value_config", ] } @@ -191,8 +206,8 @@ if (rtc_include_tests) { "../api/transport:stun_types", "../rtc_base", "../rtc_base:async_resolver_interface", + "../rtc_base:copy_on_write_buffer", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket", "../rtc_base:socket_address", @@ -236,6 +251,7 @@ if (rtc_include_tests) { ":p2p_server_utils", ":p2p_test_utils", ":rtc_p2p", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:mock_async_dns_resolver", "../api:packet_socket_factory", @@ -243,22 +259,29 @@ if (rtc_include_tests) { "../api/transport:stun_types", "../api/units:time_delta", "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:byte_buffer", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", "../rtc_base:gunit_helpers", "../rtc_base:ip_address", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:net_helpers", "../rtc_base:network_constants", - "../rtc_base:rtc_base_approved", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket", "../rtc_base:socket_address", "../rtc_base:testclient", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/network:sent_packet", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", - "../test:field_trial", "../test:rtc_expect_death", + "../test:scoped_key_value_config", "../test:test_support", "//testing/gtest", ] @@ -280,13 +303,17 @@ rtc_library("p2p_server_utils") { ] deps = [ ":rtc_p2p", + "../api:array_view", "../api:packet_socket_factory", "../api:sequence_checker", "../api/transport:stun_types", "../rtc_base", + "../rtc_base:byte_buffer", "../rtc_base:checks", + "../rtc_base:logging", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket_address", + "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/sigslot", @@ -311,10 +338,13 @@ rtc_library("libstunprober") { "../api/transport:stun_types", "../rtc_base", "../rtc_base:async_resolver_interface", + "../rtc_base:byte_buffer", "../rtc_base:checks", "../rtc_base:ip_address", + "../rtc_base:logging", "../rtc_base:socket_address", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/system:rtc_export", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", diff --git a/p2p/base/connection.cc b/p2p/base/connection.cc index a04f318acb..25a4d012a9 100644 --- a/p2p/base/connection.cc +++ b/p2p/base/connection.cc @@ -33,7 +33,6 @@ #include "rtc_base/string_utils.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/third_party/base64/base64.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -164,58 +163,59 @@ constexpr int kSupportGoogPingVersionResponseIndex = namespace cricket { // A ConnectionRequest is a STUN binding used to determine writability. -ConnectionRequest::ConnectionRequest(Connection* connection) - : StunRequest(new IceMessage()), connection_(connection) {} +ConnectionRequest::ConnectionRequest(StunRequestManager& manager, + Connection* connection) + : StunRequest(manager, std::make_unique()), + connection_(connection) {} -void ConnectionRequest::Prepare(StunMessage* request) { +void ConnectionRequest::Prepare(StunMessage* message) { RTC_DCHECK_RUN_ON(connection_->network_thread_); - request->SetType(STUN_BINDING_REQUEST); + message->SetType(STUN_BINDING_REQUEST); std::string username; connection_->port()->CreateStunUsername( connection_->remote_candidate().username(), &username); // Note that the order of attributes does not impact the parsing on the // receiver side. The attribute is retrieved then by iterating and matching // over all parsed attributes. See StunMessage::GetAttribute. - request->AddAttribute( + message->AddAttribute( std::make_unique(STUN_ATTR_USERNAME, username)); // connection_ already holds this ping, so subtract one from count. if (connection_->port()->send_retransmit_count_attribute()) { - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_RETRANSMIT_COUNT, static_cast(connection_->pings_since_last_response_.size() - 1))); } uint32_t network_info = connection_->port()->Network()->id(); network_info = (network_info << 16) | connection_->port()->network_cost(); - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_GOOG_NETWORK_INFO, network_info)); - if (webrtc::field_trial::IsEnabled( - "WebRTC-PiggybackIceCheckAcknowledgement") && + if (connection_->field_trials_->piggyback_ice_check_acknowledgement && connection_->last_ping_id_received()) { - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_GOOG_LAST_ICE_CHECK_RECEIVED, connection_->last_ping_id_received().value())); } // Adding ICE_CONTROLLED or ICE_CONTROLLING attribute based on the role. if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLING) { - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_ICE_CONTROLLING, connection_->port()->IceTiebreaker())); // We should have either USE_CANDIDATE attribute or ICE_NOMINATION // attribute but not both. That was enforced in p2ptransportchannel. if (connection_->use_candidate_attr()) { - request->AddAttribute( + message->AddAttribute( std::make_unique(STUN_ATTR_USE_CANDIDATE)); } if (connection_->nomination_ && connection_->nomination_ != connection_->acked_nomination()) { - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_NOMINATION, connection_->nomination_)); } } else if (connection_->port()->GetIceRole() == ICEROLE_CONTROLLED) { - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_ICE_CONTROLLED, connection_->port()->IceTiebreaker())); } else { RTC_DCHECK_NOTREACHED(); @@ -234,7 +234,7 @@ void ConnectionRequest::Prepare(StunMessage* request) { uint32_t prflx_priority = type_preference << 24 | (connection_->local_candidate().priority() & 0x00FFFFFF); - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_PRIORITY, prflx_priority)); if (connection_->field_trials_->enable_goog_ping && @@ -245,16 +245,16 @@ void ConnectionRequest::Prepare(StunMessage* request) { auto list = StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO); list->AddTypeAtIndex(kSupportGoogPingVersionRequestIndex, kGoogPingVersion); - request->AddAttribute(std::move(list)); + message->AddAttribute(std::move(list)); } - if (connection_->ShouldSendGoogPing(request)) { - request->SetType(GOOG_PING_REQUEST); - request->ClearAttributes(); - request->AddMessageIntegrity32(connection_->remote_candidate().password()); + if (connection_->ShouldSendGoogPing(message)) { + message->SetType(GOOG_PING_REQUEST); + message->ClearAttributes(); + message->AddMessageIntegrity32(connection_->remote_candidate().password()); } else { - request->AddMessageIntegrity(connection_->remote_candidate().password()); - request->AddFingerprint(); + message->AddMessageIntegrity(connection_->remote_candidate().password()); + message->AddFingerprint(); } } @@ -278,19 +278,19 @@ void ConnectionRequest::OnSent() { connection_->OnConnectionRequestSent(this); // Each request is sent only once. After a single delay , the request will // time out. - timeout_ = true; + set_timed_out(); } int ConnectionRequest::resend_delay() { return CONNECTION_RESPONSE_TIMEOUT; } -Connection::Connection(Port* port, +Connection::Connection(rtc::WeakPtr port, size_t index, const Candidate& remote_candidate) : network_thread_(port->thread()), id_(rtc::CreateRandomId()), - port_(port), + port_(std::move(port)), local_candidate_index_(index), remote_candidate_(remote_candidate), recv_rate_tracker_(100, 10u), @@ -300,7 +300,10 @@ Connection::Connection(Port* port, connected_(true), pruned_(false), use_candidate_attr_(false), - requests_(port->thread()), + requests_(port_->thread(), + [this](const void* data, size_t size, StunRequest* request) { + OnSendStunPacket(data, size, request); + }), rtt_(DEFAULT_RTT), last_ping_sent_(0), last_ping_received_(0), @@ -312,10 +315,7 @@ Connection::Connection(Port* port, field_trials_(&kDefaultFieldTrials), rtt_estimate_(DEFAULT_RTT_ESTIMATE_HALF_TIME_MS) { RTC_DCHECK_RUN_ON(network_thread_); - // All of our connections start in WAITING state. - // TODO(mallinath) - Start connections from STATE_FROZEN. - // Wire up to send stun packets - requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket); + RTC_DCHECK(port_); RTC_LOG(LS_INFO) << ToString() << ": Connection created"; } @@ -576,7 +576,7 @@ void Connection::OnReadPacket(const char* data, if (msg->IntegrityOk()) { requests_.CheckResponse(msg.get()); } - // Otherwise silently discard the response message. + // Otherwise silently discard the response. break; // Remote end point sent an STUN indication instead of regular binding @@ -605,8 +605,7 @@ void Connection::HandleStunBindingOrGoogPingRequest(IceMessage* msg) { RTC_DCHECK_RUN_ON(network_thread_); // This connection should now be receiving. ReceivedPing(msg->transaction_id()); - if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing") && - last_ping_response_received_ == 0) { + if (field_trials_->extra_ice_ping && last_ping_response_received_ == 0) { if (local_candidate().type() == RELAY_PORT_TYPE || local_candidate().type() == PRFLX_PORT_TYPE || remote_candidate().type() == RELAY_PORT_TYPE || @@ -695,31 +694,30 @@ void Connection::HandleStunBindingOrGoogPingRequest(IceMessage* msg) { } } - if (webrtc::field_trial::IsEnabled( - "WebRTC-PiggybackIceCheckAcknowledgement")) { + if (field_trials_->piggyback_ice_check_acknowledgement) { HandlePiggybackCheckAcknowledgementIfAny(msg); } } -void Connection::SendStunBindingResponse(const StunMessage* request) { +void Connection::SendStunBindingResponse(const StunMessage* message) { RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(request->type() == STUN_BINDING_REQUEST); + RTC_DCHECK_EQ(message->type(), STUN_BINDING_REQUEST); - // Retrieve the username from the request. + // Retrieve the username from the `message`. const StunByteStringAttribute* username_attr = - request->GetByteString(STUN_ATTR_USERNAME); + message->GetByteString(STUN_ATTR_USERNAME); RTC_DCHECK(username_attr != NULL); if (username_attr == NULL) { // No valid username, skip the response. return; } - // Fill in the response message. + // Fill in the response. StunMessage response; response.SetType(STUN_BINDING_RESPONSE); - response.SetTransactionID(request->transaction_id()); + response.SetTransactionID(message->transaction_id()); const StunUInt32Attribute* retransmit_attr = - request->GetUInt32(STUN_ATTR_RETRANSMIT_COUNT); + message->GetUInt32(STUN_ATTR_RETRANSMIT_COUNT); if (retransmit_attr) { // Inherit the incoming retransmit value in the response so the other side // can see our view of lost pings. @@ -738,8 +736,8 @@ void Connection::SendStunBindingResponse(const StunMessage* request) { STUN_ATTR_XOR_MAPPED_ADDRESS, remote_candidate_.address())); if (field_trials_->announce_goog_ping) { - // Check if request contains a announce-request. - auto goog_misc = request->GetUInt16List(STUN_ATTR_GOOG_MISC_INFO); + // Check if message contains a announce-request. + auto goog_misc = message->GetUInt16List(STUN_ATTR_GOOG_MISC_INFO); if (goog_misc != nullptr && goog_misc->Size() >= kSupportGoogPingVersionRequestIndex && // Which version can we handle...currently any >= 1 @@ -758,14 +756,14 @@ void Connection::SendStunBindingResponse(const StunMessage* request) { SendResponseMessage(response); } -void Connection::SendGoogPingResponse(const StunMessage* request) { +void Connection::SendGoogPingResponse(const StunMessage* message) { RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(request->type() == GOOG_PING_REQUEST); + RTC_DCHECK(message->type() == GOOG_PING_REQUEST); - // Fill in the response message. + // Fill in the response. StunMessage response; response.SetType(GOOG_PING_RESPONSE); - response.SetTransactionID(request->transaction_id()); + response.SetTransactionID(message->transaction_id()); response.AddMessageIntegrity32(local_candidate().password()); SendResponseMessage(response); } @@ -775,7 +773,7 @@ void Connection::SendResponseMessage(const StunMessage& response) { // Where I send the response. const rtc::SocketAddress& addr = remote_candidate_.address(); - // Send the response message. + // Send the response. rtc::ByteBufferWriter buf; response.Write(&buf); rtc::PacketOptions options(port_->StunDscpValue()); @@ -834,6 +832,9 @@ void Connection::Prune() { void Connection::Destroy() { RTC_DCHECK_RUN_ON(network_thread_); + if (!port_) + return; + RTC_DLOG(LS_VERBOSE) << ToString() << ": Connection destroyed"; // Fire the 'destroyed' event before deleting the object. This is done @@ -845,6 +846,10 @@ void Connection::Destroy() { LogCandidatePairConfig(webrtc::IceCandidatePairConfigType::kDestroyed); + // Reset the `port_` after logging and firing the destroyed signal since + // information required for logging needs access to `port_`. + port_.reset(); + // Unwind the stack before deleting the object in case upstream callers // need to refer to the Connection's state as part of teardown. // NOTE: We move ownership of 'this' into the capture section of the lambda @@ -863,6 +868,18 @@ void Connection::FailAndDestroy() { void Connection::FailAndPrune() { RTC_DCHECK_RUN_ON(network_thread_); + + // TODO(bugs.webrtc.org/13865): There's a circular dependency between Port + // and Connection. In some cases (Port dtor), a Connection object is deleted + // without using the `Destroy` method (port_ won't be nulled and some + // functionality won't run as expected), while in other cases + // (AddOrReplaceConnection), the Connection object is deleted asynchronously + // via the `Destroy` method and in that case `port_` will be nulled. + // However, in that case, there's a chance that the Port object gets + // deleted before the Connection object ends up being deleted. + if (!port_) + return; + set_state(IceCandidatePairState::FAILED); Prune(); } @@ -970,7 +987,7 @@ int64_t Connection::last_ping_sent() const { void Connection::Ping(int64_t now) { RTC_DCHECK_RUN_ON(network_thread_); last_ping_sent_ = now; - ConnectionRequest* req = new ConnectionRequest(this); + ConnectionRequest* req = new ConnectionRequest(requests_, this); // If not using renomination, we use "1" to mean "nominated" and "0" to mean // "not nominated". If using renomination, values greater than 1 are used for // re-nominated pairs. @@ -1182,49 +1199,65 @@ uint32_t Connection::ComputeNetworkCost() const { std::string Connection::ToString() const { RTC_DCHECK_RUN_ON(network_thread_); - const absl::string_view CONNECT_STATE_ABBREV[2] = { + constexpr absl::string_view CONNECT_STATE_ABBREV[2] = { "-", // not connected (false) "C", // connected (true) }; - const absl::string_view RECEIVE_STATE_ABBREV[2] = { + constexpr absl::string_view RECEIVE_STATE_ABBREV[2] = { "-", // not receiving (false) "R", // receiving (true) }; - const absl::string_view WRITE_STATE_ABBREV[4] = { + constexpr absl::string_view WRITE_STATE_ABBREV[4] = { "W", // STATE_WRITABLE "w", // STATE_WRITE_UNRELIABLE "-", // STATE_WRITE_INIT "x", // STATE_WRITE_TIMEOUT }; - const absl::string_view ICESTATE[4] = { + constexpr absl::string_view ICESTATE[4] = { "W", // STATE_WAITING "I", // STATE_INPROGRESS "S", // STATE_SUCCEEDED "F" // STATE_FAILED }; - const absl::string_view SELECTED_STATE_ABBREV[2] = { + constexpr absl::string_view SELECTED_STATE_ABBREV[2] = { "-", // candidate pair not selected (false) "S", // selected (true) }; - const Candidate& local = local_candidate(); - const Candidate& remote = remote_candidate(); rtc::StringBuilder ss; - ss << "Conn[" << ToDebugId() << ":" << port_->content_name() << ":" - << port_->Network()->ToString() << ":" << local.id() << ":" - << local.component() << ":" << local.generation() << ":" << local.type() - << ":" << local.protocol() << ":" << local.address().ToSensitiveString() - << "->" << remote.id() << ":" << remote.component() << ":" - << remote.priority() << ":" << remote.type() << ":" << remote.protocol() - << ":" << remote.address().ToSensitiveString() << "|" - << CONNECT_STATE_ABBREV[connected()] << RECEIVE_STATE_ABBREV[receiving()] - << WRITE_STATE_ABBREV[write_state()] << ICESTATE[static_cast(state())] - << "|" << SELECTED_STATE_ABBREV[selected_] << "|" << remote_nomination() - << "|" << nomination_ << "|" << priority() << "|"; + ss << "Conn[" << ToDebugId(); + + if (!port_) { + // No content name for pending delete, so temporarily substitute the name + // with a hash (rhyming with trash) and don't include any information about + // the network or candidates, state that belongs to a potentially deleted + // `port_`. + ss << ":#:"; + } else { + const Candidate& local = local_candidate(); + const Candidate& remote = remote_candidate(); + ss << ":" << port_->content_name() << ":" << port_->Network()->ToString() + << ":" << local.id() << ":" << local.component() << ":" + << local.generation() << ":" << local.type() << ":" << local.protocol() + << ":" << local.address().ToSensitiveString() << "->" << remote.id() + << ":" << remote.component() << ":" << remote.priority() << ":" + << remote.type() << ":" << remote.protocol() << ":" + << remote.address().ToSensitiveString() << "|"; + } + + ss << CONNECT_STATE_ABBREV[connected_] << RECEIVE_STATE_ABBREV[receiving_] + << WRITE_STATE_ABBREV[write_state_] << ICESTATE[static_cast(state_)] + << "|" << SELECTED_STATE_ABBREV[selected_] << "|" << remote_nomination_ + << "|" << nomination_ << "|"; + + if (port_) + ss << priority() << "|"; + if (rtt_ < DEFAULT_RTT) { ss << rtt_ << "]"; } else { ss << "-]"; } + return ss.Release(); } @@ -1390,7 +1423,7 @@ void Connection::OnConnectionRequestSent(ConnectionRequest* request) { } void Connection::HandleRoleConflictFromPeer() { - port_->SignalRoleConflict(port_); + port_->SignalRoleConflict(port()); } IceCandidatePairState Connection::state() const { @@ -1586,10 +1619,10 @@ void Connection::ForgetLearnedState() { pings_since_last_response_.clear(); } -ProxyConnection::ProxyConnection(Port* port, +ProxyConnection::ProxyConnection(rtc::WeakPtr port, size_t index, const Candidate& remote_candidate) - : Connection(port, index, remote_candidate) {} + : Connection(std::move(port), index, remote_candidate) {} int ProxyConnection::Send(const void* data, size_t size, diff --git a/p2p/base/connection.h b/p2p/base/connection.h index 8254706318..8d22da2188 100644 --- a/p2p/base/connection.h +++ b/p2p/base/connection.h @@ -29,6 +29,7 @@ #include "rtc_base/network.h" #include "rtc_base/numerics/event_based_exponential_moving_average.h" #include "rtc_base/rate_tracker.h" +#include "rtc_base/weak_ptr.h" namespace cricket { @@ -56,8 +57,8 @@ struct CandidatePair final : public CandidatePairInterface { // A ConnectionRequest is a simple STUN ping used to determine writability. class ConnectionRequest : public StunRequest { public: - explicit ConnectionRequest(Connection* connection); - void Prepare(StunMessage* request) override; + ConnectionRequest(StunRequestManager& manager, Connection* connection); + void Prepare(StunMessage* message) override; void OnResponse(StunMessage* response) override; void OnErrorResponse(StunMessage* response) override; void OnTimeout() override; @@ -70,7 +71,7 @@ class ConnectionRequest : public StunRequest { // Represents a communication link between a port on the local client and a // port on the remote client. -class Connection : public CandidatePairInterface, public sigslot::has_slots<> { +class Connection : public CandidatePairInterface { public: struct SentPing { SentPing(const std::string id, int64_t sent_time, uint32_t nomination) @@ -305,13 +306,13 @@ class Connection : public CandidatePairInterface, public sigslot::has_slots<> { // Does not trigger SignalStateChange void ForgetLearnedState(); - void SendStunBindingResponse(const StunMessage* request); - void SendGoogPingResponse(const StunMessage* request); + void SendStunBindingResponse(const StunMessage* message); + void SendGoogPingResponse(const StunMessage* message); void SendResponseMessage(const StunMessage& response); // An accessor for unit tests. - Port* PortForTest() { return port_; } - const Port* PortForTest() const { return port_; } + Port* PortForTest() { return port_.get(); } + const Port* PortForTest() const { return port_.get(); } // Public for unit tests. uint32_t acked_nomination() const; @@ -319,7 +320,7 @@ class Connection : public CandidatePairInterface, public sigslot::has_slots<> { protected: // Constructs a new connection to the given remote port. - Connection(Port* port, size_t index, const Candidate& candidate); + Connection(rtc::WeakPtr port, size_t index, const Candidate& candidate); // Called back when StunRequestManager has a stun packet to send void OnSendStunPacket(const void* data, size_t size, StunRequest* req); @@ -348,8 +349,8 @@ class Connection : public CandidatePairInterface, public sigslot::has_slots<> { void set_connected(bool value); // The local port where this connection sends and receives packets. - Port* port() { return port_; } - const Port* port() const { return port_; } + Port* port() { return port_.get(); } + const Port* port() const { return port_.get(); } // NOTE: A pointer to the network thread is held by `port_` so in theory we // shouldn't need to hold on to this pointer here, but rather defer to @@ -358,7 +359,7 @@ class Connection : public CandidatePairInterface, public sigslot::has_slots<> { // TODO(tommi): This ^^^ should be fixed. webrtc::TaskQueueBase* const network_thread_; const uint32_t id_; - Port* const port_; + rtc::WeakPtr port_; size_t local_candidate_index_ RTC_GUARDED_BY(network_thread_); Candidate remote_candidate_; @@ -467,7 +468,9 @@ class Connection : public CandidatePairInterface, public sigslot::has_slots<> { // ProxyConnection defers all the interesting work to the port. class ProxyConnection : public Connection { public: - ProxyConnection(Port* port, size_t index, const Candidate& remote_candidate); + ProxyConnection(rtc::WeakPtr port, + size_t index, + const Candidate& remote_candidate); int Send(const void* data, size_t size, diff --git a/p2p/base/default_ice_transport_factory.cc b/p2p/base/default_ice_transport_factory.cc index 0a7175cfd8..8d87d66697 100644 --- a/p2p/base/default_ice_transport_factory.cc +++ b/p2p/base/default_ice_transport_factory.cc @@ -44,10 +44,10 @@ DefaultIceTransportFactory::CreateIceTransport( int component, IceTransportInit init) { BasicIceControllerFactory factory; + init.set_ice_controller_factory(&factory); return rtc::make_ref_counted( - cricket::P2PTransportChannel::Create( - transport_name, component, init.port_allocator(), - init.async_dns_resolver_factory(), init.event_log(), &factory)); + cricket::P2PTransportChannel::Create(transport_name, component, + std::move(init))); } } // namespace webrtc diff --git a/p2p/base/fake_port_allocator.h b/p2p/base/fake_port_allocator.h index 6366ea84db..59533faab0 100644 --- a/p2p/base/fake_port_allocator.h +++ b/p2p/base/fake_port_allocator.h @@ -20,6 +20,7 @@ #include "p2p/base/udp_port.h" #include "rtc_base/net_helpers.h" #include "rtc_base/thread.h" +#include "test/scoped_key_value_config.h" namespace rtc { class SocketFactory; @@ -31,15 +32,16 @@ class TestUDPPort : public UDPPort { public: static TestUDPPort* Create(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool emit_localhost_for_anyaddress) { + bool emit_localhost_for_anyaddress, + const webrtc::FieldTrialsView* field_trials) { TestUDPPort* port = new TestUDPPort(thread, factory, network, min_port, max_port, username, - password, emit_localhost_for_anyaddress); + password, emit_localhost_for_anyaddress, field_trials); if (!port->Init()) { delete port; port = nullptr; @@ -50,12 +52,13 @@ class TestUDPPort : public UDPPort { protected: TestUDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool emit_localhost_for_anyaddress) + bool emit_localhost_for_anyaddress, + const webrtc::FieldTrialsView* field_trials) : UDPPort(thread, factory, network, @@ -63,7 +66,8 @@ class TestUDPPort : public UDPPort { max_port, username, password, - emit_localhost_for_anyaddress) {} + emit_localhost_for_anyaddress, + field_trials) {} }; // A FakePortAllocatorSession can be used with either a real or fake socket @@ -77,7 +81,8 @@ class FakePortAllocatorSession : public PortAllocatorSession { const std::string& content_name, int component, const std::string& ice_ufrag, - const std::string& ice_pwd) + const std::string& ice_pwd, + const webrtc::FieldTrialsView& field_trials) : PortAllocatorSession(content_name, component, ice_ufrag, @@ -96,7 +101,8 @@ class FakePortAllocatorSession : public PortAllocatorSession { port_(), port_config_count_(0), stun_servers_(allocator->stun_servers()), - turn_servers_(allocator->turn_servers()) { + turn_servers_(allocator->turn_servers()), + field_trials_(field_trials) { ipv4_network_.AddIP(rtc::IPAddress(INADDR_LOOPBACK)); ipv6_network_.AddIP(rtc::IPAddress(in6addr_loopback)); } @@ -112,7 +118,8 @@ class FakePortAllocatorSession : public PortAllocatorSession { ? ipv6_network_ : ipv4_network_; port_.reset(TestUDPPort::Create(network_thread_, factory_, &network, 0, 0, - username(), password(), false)); + username(), password(), false, + &field_trials_)); RTC_DCHECK(port_); port_->SubscribePortDestroyed( [this](PortInterface* port) { OnPortDestroyed(port); }); @@ -200,6 +207,7 @@ class FakePortAllocatorSession : public PortAllocatorSession { uint32_t candidate_filter_ = CF_ALL; int transport_info_update_count_ = 0; bool running_ = false; + const webrtc::FieldTrialsView& field_trials_; }; class FakePortAllocator : public cricket::PortAllocator { @@ -231,7 +239,7 @@ class FakePortAllocator : public cricket::PortAllocator { const std::string& ice_pwd) override { return new FakePortAllocatorSession(this, network_thread_, factory_, content_name, component, ice_ufrag, - ice_pwd); + ice_pwd, field_trials_); } bool initialized() const { return initialized_; } @@ -245,6 +253,7 @@ class FakePortAllocator : public cricket::PortAllocator { } private: + webrtc::test::ScopedKeyValueConfig field_trials_; rtc::Thread* network_thread_; rtc::PacketSocketFactory* factory_; std::unique_ptr owned_factory_; diff --git a/p2p/base/ice_transport_internal.h b/p2p/base/ice_transport_internal.h index 20730e1cfd..4f74ac7ae8 100644 --- a/p2p/base/ice_transport_internal.h +++ b/p2p/base/ice_transport_internal.h @@ -38,6 +38,19 @@ struct IceTransportStats { // Initially 0 and 1 once the first candidate pair has been selected. // The counter is increase also when "unselecting" a connection. uint32_t selected_candidate_pair_changes = 0; + + // Bytes/packets sent/received. + // note: Is not the same as sum(connection_infos.bytes_sent) + // as connections are created and destroyed while the ICE transport + // is alive. + uint64_t bytes_sent = 0; + uint64_t bytes_received = 0; + uint64_t packets_sent = 0; + uint64_t packets_received = 0; + + IceRole ice_role = ICEROLE_UNKNOWN; + std::string ice_local_username_fragment; + webrtc::IceTransportState ice_state = webrtc::IceTransportState::kNew; }; typedef std::vector Candidates; diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc index 9fe9cac948..47163e4e33 100644 --- a/p2p/base/p2p_transport_channel.cc +++ b/p2p/base/p2p_transport_channel.cc @@ -24,6 +24,7 @@ #include "absl/strings/match.h" #include "api/async_dns_resolver.h" #include "api/candidate.h" +#include "api/field_trials_view.h" #include "api/task_queue/queued_task.h" #include "logging/rtc_event_log/ice_logger.h" #include "p2p/base/basic_async_resolver_factory.h" @@ -44,7 +45,6 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" namespace { @@ -60,12 +60,15 @@ cricket::PortInterface::CandidateOrigin GetOrigin( return cricket::PortInterface::ORIGIN_OTHER_PORT; } -uint32_t GetWeakPingIntervalInFieldTrial() { - uint32_t weak_ping_interval = ::strtoul( - webrtc::field_trial::FindFullName("WebRTC-StunInterPacketDelay").c_str(), - nullptr, 10); - if (weak_ping_interval) { - return static_cast(weak_ping_interval); +uint32_t GetWeakPingIntervalInFieldTrial( + const webrtc::FieldTrialsView* field_trials) { + if (field_trials != nullptr) { + uint32_t weak_ping_interval = + ::strtoul(field_trials->Lookup("WebRTC-StunInterPacketDelay").c_str(), + nullptr, 10); + if (weak_ping_interval) { + return static_cast(weak_ping_interval); + } } return cricket::WEAK_PING_INTERVAL; } @@ -109,30 +112,37 @@ bool IceCredentialsChanged(const std::string& old_ufrag, return (old_ufrag != new_ufrag) || (old_pwd != new_pwd); } -// static std::unique_ptr P2PTransportChannel::Create( const std::string& transport_name, int component, - PortAllocator* allocator, - webrtc::AsyncDnsResolverFactoryInterface* async_dns_resolver_factory, - webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) { - return absl::WrapUnique(new P2PTransportChannel( - transport_name, component, allocator, async_dns_resolver_factory, - /* owned_dns_resolver_factory= */ nullptr, event_log, - ice_controller_factory)); + webrtc::IceTransportInit init) { + if (init.async_resolver_factory()) { + return absl::WrapUnique(new P2PTransportChannel( + transport_name, component, init.port_allocator(), nullptr, + std::make_unique( + init.async_resolver_factory()), + init.event_log(), init.ice_controller_factory(), init.field_trials())); + } else { + return absl::WrapUnique(new P2PTransportChannel( + transport_name, component, init.port_allocator(), + init.async_dns_resolver_factory(), nullptr, init.event_log(), + init.ice_controller_factory(), init.field_trials())); + } } -P2PTransportChannel::P2PTransportChannel(const std::string& transport_name, - int component, - PortAllocator* allocator) +P2PTransportChannel::P2PTransportChannel( + const std::string& transport_name, + int component, + PortAllocator* allocator, + const webrtc::FieldTrialsView* field_trials) : P2PTransportChannel(transport_name, component, allocator, /* async_dns_resolver_factory= */ nullptr, /* owned_dns_resolver_factory= */ nullptr, /* event_log= */ nullptr, - /* ice_controller_factory= */ nullptr) {} + /* ice_controller_factory= */ nullptr, + field_trials) {} // Private constructor, called from Create() P2PTransportChannel::P2PTransportChannel( @@ -143,7 +153,8 @@ P2PTransportChannel::P2PTransportChannel( std::unique_ptr owned_dns_resolver_factory, webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) + IceControllerFactoryInterface* ice_controller_factory, + const webrtc::FieldTrialsView* field_trials) : transport_name_(transport_name), component_(component), allocator_(allocator), @@ -161,6 +172,7 @@ P2PTransportChannel::P2PTransportChannel( ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), gathering_state_(kIceGatheringNew), + weak_ping_interval_(GetWeakPingIntervalInFieldTrial(field_trials)), config_(RECEIVING_TIMEOUT, BACKUP_CONNECTION_PING_INTERVAL, GATHER_ONCE /* continual_gathering_policy */, @@ -171,7 +183,6 @@ P2PTransportChannel::P2PTransportChannel( RECEIVING_SWITCHING_DELAY) { TRACE_EVENT0("webrtc", "P2PTransportChannel::P2PTransportChannel"); RTC_DCHECK(allocator_ != nullptr); - weak_ping_interval_ = GetWeakPingIntervalInFieldTrial(); // Validate IceConfig even for mostly built-in constant default values in case // we change them. RTC_DCHECK(ValidateIceConfig(config_).ok()); @@ -187,6 +198,8 @@ P2PTransportChannel::P2PTransportChannel( this, &P2PTransportChannel::OnCandidateFilterChanged); ice_event_log_.set_event_log(event_log); + ParseFieldTrials(field_trials); + IceControllerFactoryArgs args{ [this] { return GetState(); }, [this] { return GetIceRole(); }, [this](const Connection* connection) { @@ -195,8 +208,9 @@ P2PTransportChannel::P2PTransportChannel( return IsPortPruned(connection->port()) || IsRemoteCandidatePruned(connection->remote_candidate()); }, - &field_trials_, - webrtc::field_trial::FindFullName("WebRTC-IceControllerFieldTrials")}; + &ice_field_trials_, + field_trials ? field_trials->Lookup("WebRTC-IceControllerFieldTrials") + : ""}; if (ice_controller_factory != nullptr) { ice_controller_ = ice_controller_factory->Create(args); } else { @@ -204,25 +218,6 @@ P2PTransportChannel::P2PTransportChannel( } } -// Public constructor, exposed for backwards compatibility. -// Deprecated. -P2PTransportChannel::P2PTransportChannel( - const std::string& transport_name, - int component, - PortAllocator* allocator, - webrtc::AsyncResolverFactory* async_resolver_factory, - webrtc::RtcEventLog* event_log, - IceControllerFactoryInterface* ice_controller_factory) - : P2PTransportChannel( - transport_name, - component, - allocator, - nullptr, - std::make_unique( - async_resolver_factory), - event_log, - ice_controller_factory) {} - P2PTransportChannel::~P2PTransportChannel() { TRACE_EVENT0("webrtc", "P2PTransportChannel::~P2PTransportChannel"); RTC_DCHECK_RUN_ON(network_thread_); @@ -282,7 +277,7 @@ void P2PTransportChannel::AddConnection(Connection* connection) { had_connection_ = true; connection->set_ice_event_log(&ice_event_log_); - connection->SetIceFieldTrials(&field_trials_); + connection->SetIceFieldTrials(&ice_field_trials_); LogCandidatePairConfig(connection, webrtc::IceCandidatePairConfigType::kAdded); @@ -700,93 +695,118 @@ void P2PTransportChannel::SetIceConfig(const IceConfig& config) { << config.stun_keepalive_interval_or_default(); } - if (webrtc::field_trial::IsEnabled("WebRTC-ExtraICEPing")) { + webrtc::BasicRegatheringController::Config regathering_config; + regathering_config.regather_on_failed_networks_interval = + config_.regather_on_failed_networks_interval_or_default(); + regathering_controller_->SetConfig(regathering_config); + + config_.vpn_preference = config.vpn_preference; + allocator_->SetVpnPreference(config_.vpn_preference); + + ice_controller_->SetIceConfig(config_); + + RTC_DCHECK(ValidateIceConfig(config_).ok()); +} + +void P2PTransportChannel::ParseFieldTrials( + const webrtc::FieldTrialsView* field_trials) { + if (field_trials == nullptr) { + return; + } + + if (field_trials->IsEnabled("WebRTC-ExtraICEPing")) { RTC_LOG(LS_INFO) << "Set WebRTC-ExtraICEPing: Enabled"; } - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { + if (field_trials->IsEnabled("WebRTC-TurnAddMultiMapping")) { RTC_LOG(LS_INFO) << "Set WebRTC-TurnAddMultiMapping: Enabled"; } webrtc::StructParametersParser::Create( // go/skylift-light "skip_relay_to_non_relay_connections", - &field_trials_.skip_relay_to_non_relay_connections, + &ice_field_trials_.skip_relay_to_non_relay_connections, // Limiting pings sent. - "max_outstanding_pings", &field_trials_.max_outstanding_pings, + "max_outstanding_pings", &ice_field_trials_.max_outstanding_pings, // Delay initial selection of connection. - "initial_select_dampening", &field_trials_.initial_select_dampening, + "initial_select_dampening", &ice_field_trials_.initial_select_dampening, // Delay initial selection of connections, that are receiving. "initial_select_dampening_ping_received", - &field_trials_.initial_select_dampening_ping_received, + &ice_field_trials_.initial_select_dampening_ping_received, // Reply that we support goog ping. - "announce_goog_ping", &field_trials_.announce_goog_ping, + "announce_goog_ping", &ice_field_trials_.announce_goog_ping, // Use goog ping if remote support it. - "enable_goog_ping", &field_trials_.enable_goog_ping, + "enable_goog_ping", &ice_field_trials_.enable_goog_ping, // How fast does a RTT sample decay. - "rtt_estimate_halftime_ms", &field_trials_.rtt_estimate_halftime_ms, + "rtt_estimate_halftime_ms", &ice_field_trials_.rtt_estimate_halftime_ms, // Make sure that nomination reaching ICE controlled asap. "send_ping_on_switch_ice_controlling", - &field_trials_.send_ping_on_switch_ice_controlling, + &ice_field_trials_.send_ping_on_switch_ice_controlling, // Make sure that nomination reaching ICE controlled asap. "send_ping_on_selected_ice_controlling", - &field_trials_.send_ping_on_selected_ice_controlling, + &ice_field_trials_.send_ping_on_selected_ice_controlling, // Reply to nomination ASAP. "send_ping_on_nomination_ice_controlled", - &field_trials_.send_ping_on_nomination_ice_controlled, + &ice_field_trials_.send_ping_on_nomination_ice_controlled, // Allow connections to live untouched longer that 30s. - "dead_connection_timeout_ms", &field_trials_.dead_connection_timeout_ms, + "dead_connection_timeout_ms", + &ice_field_trials_.dead_connection_timeout_ms, // Stop gathering on strongly connected. "stop_gather_on_strongly_connected", - &field_trials_.stop_gather_on_strongly_connected) - ->Parse(webrtc::field_trial::FindFullName("WebRTC-IceFieldTrials")); + &ice_field_trials_.stop_gather_on_strongly_connected) + ->Parse(field_trials->Lookup("WebRTC-IceFieldTrials")); - if (field_trials_.dead_connection_timeout_ms < 30000) { + if (ice_field_trials_.dead_connection_timeout_ms < 30000) { RTC_LOG(LS_WARNING) << "dead_connection_timeout_ms set to " - << field_trials_.dead_connection_timeout_ms + << ice_field_trials_.dead_connection_timeout_ms << " increasing it to 30000"; - field_trials_.dead_connection_timeout_ms = 30000; + ice_field_trials_.dead_connection_timeout_ms = 30000; } - if (field_trials_.skip_relay_to_non_relay_connections) { + if (ice_field_trials_.skip_relay_to_non_relay_connections) { RTC_LOG(LS_INFO) << "Set skip_relay_to_non_relay_connections"; } - if (field_trials_.max_outstanding_pings.has_value()) { + if (ice_field_trials_.max_outstanding_pings.has_value()) { RTC_LOG(LS_INFO) << "Set max_outstanding_pings: " - << *field_trials_.max_outstanding_pings; + << *ice_field_trials_.max_outstanding_pings; } - if (field_trials_.initial_select_dampening.has_value()) { + if (ice_field_trials_.initial_select_dampening.has_value()) { RTC_LOG(LS_INFO) << "Set initial_select_dampening: " - << *field_trials_.initial_select_dampening; + << *ice_field_trials_.initial_select_dampening; } - if (field_trials_.initial_select_dampening_ping_received.has_value()) { - RTC_LOG(LS_INFO) << "Set initial_select_dampening_ping_received: " - << *field_trials_.initial_select_dampening_ping_received; + if (ice_field_trials_.initial_select_dampening_ping_received.has_value()) { + RTC_LOG(LS_INFO) + << "Set initial_select_dampening_ping_received: " + << *ice_field_trials_.initial_select_dampening_ping_received; } - webrtc::BasicRegatheringController::Config regathering_config; - regathering_config.regather_on_failed_networks_interval = - config_.regather_on_failed_networks_interval_or_default(); - regathering_controller_->SetConfig(regathering_config); - - config_.vpn_preference = config.vpn_preference; - allocator_->SetVpnPreference(config_.vpn_preference); - - ice_controller_->SetIceConfig(config_); - // DSCP override, allow user to specify (any) int value // that will be used for tagging all packets. webrtc::StructParametersParser::Create("override_dscp", - &field_trials_.override_dscp) - ->Parse(webrtc::field_trial::FindFullName("WebRTC-DscpFieldTrial")); + &ice_field_trials_.override_dscp) + ->Parse(field_trials->Lookup("WebRTC-DscpFieldTrial")); - if (field_trials_.override_dscp) { - SetOption(rtc::Socket::OPT_DSCP, *field_trials_.override_dscp); + if (ice_field_trials_.override_dscp) { + SetOption(rtc::Socket::OPT_DSCP, *ice_field_trials_.override_dscp); } - RTC_DCHECK(ValidateIceConfig(config_).ok()); + std::string field_trial_string = + field_trials->Lookup("WebRTC-SetSocketReceiveBuffer"); + int receive_buffer_size_kb = 0; + sscanf(field_trial_string.c_str(), "Enabled-%d", &receive_buffer_size_kb); + if (receive_buffer_size_kb > 0) { + RTC_LOG(LS_INFO) << "Set WebRTC-SetSocketReceiveBuffer: Enabled and set to " + << receive_buffer_size_kb << "kb"; + SetOption(rtc::Socket::OPT_RCVBUF, receive_buffer_size_kb * 1024); + } + + ice_field_trials_.piggyback_ice_check_acknowledgement = + field_trials->IsEnabled("WebRTC-PiggybackIceCheckAcknowledgement"); + + ice_field_trials_.extra_ice_ping = + field_trials->IsEnabled("WebRTC-ExtraICEPing"); } const IceConfig& P2PTransportChannel::config() const { @@ -800,7 +820,7 @@ const IceConfig& P2PTransportChannel::config() const { RTCError P2PTransportChannel::ValidateIceConfig(const IceConfig& config) { if (config.ice_check_interval_strong_connectivity_or_default() < config.ice_check_interval_weak_connectivity.value_or( - GetWeakPingIntervalInFieldTrial())) { + GetWeakPingIntervalInFieldTrial(nullptr))) { return RTCError(RTCErrorType::INVALID_PARAMETER, "Ping interval of candidate pairs is shorter when ICE is " "strongly connected than that when ICE is weakly " @@ -1184,7 +1204,8 @@ void P2PTransportChannel::OnNominated(Connection* conn) { return; } - if (field_trials_.send_ping_on_nomination_ice_controlled && conn != nullptr) { + if (ice_field_trials_.send_ping_on_nomination_ice_controlled && + conn != nullptr) { PingConnection(conn); MarkConnectionPinged(conn); } @@ -1424,7 +1445,7 @@ bool P2PTransportChannel::CreateConnection(PortInterface* port, return false; } - if (field_trials_.skip_relay_to_non_relay_connections) { + if (ice_field_trials_.skip_relay_to_non_relay_connections) { if ((port->Type() != remote_candidate.type()) && (port->Type() == RELAY_PORT_TYPE || remote_candidate.type() == RELAY_PORT_TYPE)) { @@ -1540,8 +1561,8 @@ void P2PTransportChannel::RememberRemoteCandidate( // port objects. int P2PTransportChannel::SetOption(rtc::Socket::Option opt, int value) { RTC_DCHECK_RUN_ON(network_thread_); - if (field_trials_.override_dscp && opt == rtc::Socket::OPT_DSCP) { - value = *field_trials_.override_dscp; + if (ice_field_trials_.override_dscp && opt == rtc::Socket::OPT_DSCP) { + value = *ice_field_trials_.override_dscp; } OptionMap::iterator it = options_.find(opt); @@ -1598,6 +1619,7 @@ int P2PTransportChannel::SendPacket(const char* data, return -1; } + packets_sent_++; last_sent_packet_id_ = options.packet_id; rtc::PacketOptions modified_options(options); modified_options.info_signaled_after_sent.packet_type = @@ -1606,7 +1628,10 @@ int P2PTransportChannel::SendPacket(const char* data, if (sent <= 0) { RTC_DCHECK(sent < 0); error_ = selected_connection_->GetError(); + return sent; } + + bytes_sent_ += sent; return sent; } @@ -1633,6 +1658,16 @@ bool P2PTransportChannel::GetStats(IceTransportStats* ice_transport_stats) { ice_transport_stats->selected_candidate_pair_changes = selected_candidate_pair_changes_; + + ice_transport_stats->bytes_sent = bytes_sent_; + ice_transport_stats->bytes_received = bytes_received_; + ice_transport_stats->packets_sent = packets_sent_; + ice_transport_stats->packets_received = packets_received_; + + ice_transport_stats->ice_role = GetIceRole(); + ice_transport_stats->ice_local_username_fragment = ice_parameters_.ufrag; + ice_transport_stats->ice_state = ComputeIceTransportState(); + return true; } @@ -1851,9 +1886,9 @@ void P2PTransportChannel::SwitchSelectedConnection(Connection* conn, } if (conn != nullptr && ice_role_ == ICEROLE_CONTROLLING && - ((field_trials_.send_ping_on_switch_ice_controlling && + ((ice_field_trials_.send_ping_on_switch_ice_controlling && old_selected_connection != nullptr) || - field_trials_.send_ping_on_selected_ice_controlling)) { + ice_field_trials_.send_ping_on_selected_ice_controlling)) { PingConnection(conn); MarkConnectionPinged(conn); } @@ -2112,7 +2147,7 @@ void P2PTransportChannel::OnConnectionStateChange(Connection* connection) { // the connection is at the latest generation. It is not enough to check // that the connection becomes weakly connected because the connection may be // changing from (writable, receiving) to (writable, not receiving). - if (field_trials_.stop_gather_on_strongly_connected) { + if (ice_field_trials_.stop_gather_on_strongly_connected) { bool strongly_connected = !connection->weak(); bool latest_generation = connection->local_candidate().generation() >= allocator_session()->generation(); @@ -2228,6 +2263,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, if (connection == selected_connection_) { // Let the client know of an incoming packet + packets_received_++; + bytes_received_ += len; RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_); last_data_received_ms_ = std::max(last_data_received_ms_, connection->last_data_received()); @@ -2239,6 +2276,8 @@ void P2PTransportChannel::OnReadPacket(Connection* connection, if (!FindConnection(connection)) return; + packets_received_++; + bytes_received_ += len; RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_); last_data_received_ms_ = std::max(last_data_received_ms_, connection->last_data_received()); diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h index d06234603c..4f0ac02a2b 100644 --- a/p2p/base/p2p_transport_channel.h +++ b/p2p/base/p2p_transport_channel.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "absl/base/attributes.h" @@ -36,12 +37,14 @@ #include "api/async_dns_resolver.h" #include "api/async_resolver_factory.h" #include "api/candidate.h" +#include "api/ice_transport_interface.h" #include "api/rtc_error.h" #include "api/sequence_checker.h" #include "api/transport/enums.h" #include "api/transport/stun.h" #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" #include "logging/rtc_event_log/ice_logger.h" +#include "p2p/base/basic_async_resolver_factory.h" #include "p2p/base/candidate_pair_interface.h" #include "p2p/base/connection.h" #include "p2p/base/ice_controller_factory_interface.h" @@ -104,23 +107,15 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { static std::unique_ptr Create( const std::string& transport_name, int component, - PortAllocator* allocator, - webrtc::AsyncDnsResolverFactoryInterface* async_dns_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + webrtc::IceTransportInit init); + // For testing only. // TODO(zstein): Remove once AsyncDnsResolverFactory is required. P2PTransportChannel(const std::string& transport_name, int component, - PortAllocator* allocator); - ABSL_DEPRECATED("bugs.webrtc.org/12598") - P2PTransportChannel( - const std::string& transport_name, - int component, - PortAllocator* allocator, - webrtc::AsyncResolverFactory* async_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + PortAllocator* allocator, + const webrtc::FieldTrialsView* field_trials = nullptr); + ~P2PTransportChannel() override; P2PTransportChannel(const P2PTransportChannel&) = delete; @@ -250,8 +245,9 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { // on release, this pointer is set. std::unique_ptr owned_dns_resolver_factory, - webrtc::RtcEventLog* event_log = nullptr, - IceControllerFactoryInterface* ice_controller_factory = nullptr); + webrtc::RtcEventLog* event_log, + IceControllerFactoryInterface* ice_controller_factory, + const webrtc::FieldTrialsView* field_trials); bool IsGettingPorts() { RTC_DCHECK_RUN_ON(network_thread_); return allocator_session()->IsGettingPorts(); @@ -403,6 +399,8 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { int64_t ComputeEstimatedDisconnectedTimeMs(int64_t now, Connection* old_connection); + void ParseFieldTrials(const webrtc::FieldTrialsView* field_trials); + webrtc::ScopedTaskSafety task_safety_; std::string transport_name_ RTC_GUARDED_BY(network_thread_); int component_ RTC_GUARDED_BY(network_thread_); @@ -490,6 +488,12 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { Candidate candidate, const webrtc::AsyncDnsResolverResult& result); + // Bytes/packets sent/received on this channel. + uint64_t bytes_sent_ = 0; + uint64_t bytes_received_ = 0; + uint64_t packets_sent_ = 0; + uint64_t packets_received_ = 0; + // Number of times the selected_connection_ has been modified. uint32_t selected_candidate_pair_changes_ = 0; @@ -497,7 +501,8 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal { // from connection->last_data_received() that uses rtc::TimeMillis(). int64_t last_data_received_ms_ = 0; - IceFieldTrials field_trials_; + // Parsed field trials. + IceFieldTrials ice_field_trials_; }; } // namespace cricket diff --git a/p2p/base/p2p_transport_channel_ice_field_trials.h b/p2p/base/p2p_transport_channel_ice_field_trials.h index f05623dd36..f19823b21e 100644 --- a/p2p/base/p2p_transport_channel_ice_field_trials.h +++ b/p2p/base/p2p_transport_channel_ice_field_trials.h @@ -19,6 +19,9 @@ namespace cricket { // put in separate file so that they can be shared e.g // with Connection. struct IceFieldTrials { + // This struct is built using the FieldTrialParser, and then not modified. + // TODO(jonaso) : Consider how members of this struct can be made const. + bool skip_relay_to_non_relay_connections = false; absl::optional max_outstanding_pings; @@ -64,6 +67,9 @@ struct IceFieldTrials { // DSCP taging. absl::optional override_dscp; + + bool piggyback_ice_check_acknowledgement = false; + bool extra_ice_ping = false; }; } // namespace cricket diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc index 0a24f8c030..04d4850b1e 100644 --- a/p2p/base/p2p_transport_channel_unittest.cc +++ b/p2p/base/p2p_transport_channel_unittest.cc @@ -42,7 +42,7 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" +#include "test/scoped_key_value_config.h" namespace { @@ -451,9 +451,13 @@ class P2PTransportChannelTestBase : public ::testing::Test, int component, const IceParameters& local_ice, const IceParameters& remote_ice) { - auto channel = P2PTransportChannel::Create( - "test content name", component, GetAllocator(endpoint), + webrtc::IceTransportInit init; + init.set_port_allocator(GetAllocator(endpoint)); + init.set_async_dns_resolver_factory( GetEndpoint(endpoint)->async_dns_resolver_factory_); + init.set_field_trials(&field_trials_); + auto channel = P2PTransportChannel::Create("test content name", component, + std::move(init)); channel->SignalReadyToSend.connect( this, &P2PTransportChannelTestBase::OnReadyToSend); channel->SignalCandidateGathered.connect( @@ -994,6 +998,8 @@ class P2PTransportChannelTestBase : public ::testing::Test, void OnNominated(Connection* conn) { nominated_ = true; } bool nominated() { return nominated_; } + webrtc::test::ScopedKeyValueConfig field_trials_; + private: std::unique_ptr vss_; std::unique_ptr nss_; @@ -1257,7 +1263,7 @@ class P2PTransportChannelTestWithFieldTrials public ::testing::WithParamInterface { public: void Test(const Result& expected) override { - webrtc::test::ScopedFieldTrials field_trials(GetParam()); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, GetParam()); P2PTransportChannelTest::Test(expected); } }; @@ -1373,6 +1379,81 @@ TEST_F(P2PTransportChannelTest, GetStats) { EXPECT_EQ(36U, best_conn_info->sent_discarded_bytes); EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes); EXPECT_EQ(10U, best_conn_info->packets_received); + + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_sent); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_received); + + DestroyChannels(); +} + +TEST_F(P2PTransportChannelTest, GetStatsSwitchConnection) { + rtc::ScopedFakeClock clock; + IceConfig continual_gathering_config = + CreateIceConfig(1000, GATHER_CONTINUALLY); + + ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags, + kDefaultPortAllocatorFlags); + + AddAddress(0, kAlternateAddrs[1], "rmnet0", rtc::ADAPTER_TYPE_CELLULAR); + + CreateChannels(continual_gathering_config, continual_gathering_config); + EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->receiving() && ep1_ch1()->writable() && + ep2_ch1()->receiving() && + ep2_ch1()->writable(), + kMediumTimeout, clock); + // Sends and receives 10 packets. + TestSendRecv(&clock); + + IceTransportStats ice_transport_stats; + ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats)); + ASSERT_GE(ice_transport_stats.connection_infos.size(), 2u); + ASSERT_GE(ice_transport_stats.candidate_stats_list.size(), 2u); + EXPECT_EQ(ice_transport_stats.selected_candidate_pair_changes, 1u); + + ConnectionInfo* best_conn_info = nullptr; + for (ConnectionInfo& info : ice_transport_stats.connection_infos) { + if (info.best_connection) { + best_conn_info = &info; + break; + } + } + ASSERT_TRUE(best_conn_info != nullptr); + EXPECT_TRUE(best_conn_info->new_connection); + EXPECT_TRUE(best_conn_info->receiving); + EXPECT_TRUE(best_conn_info->writable); + EXPECT_FALSE(best_conn_info->timeout); + + EXPECT_EQ(10 * 36U, best_conn_info->sent_total_bytes); + EXPECT_EQ(10 * 36U, best_conn_info->recv_total_bytes); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_sent); + EXPECT_EQ(10 * 36U, ice_transport_stats.bytes_received); + + auto old_selected_connection = ep1_ch1()->selected_connection(); + ep1_ch1()->RemoveConnectionForTest( + const_cast(old_selected_connection)); + + EXPECT_TRUE_SIMULATED_WAIT(ep1_ch1()->selected_connection() != nullptr, + kMediumTimeout, clock); + + // Sends and receives 10 packets. + TestSendRecv(&clock); + + IceTransportStats ice_transport_stats2; + ASSERT_TRUE(ep1_ch1()->GetStats(&ice_transport_stats2)); + + int64_t sum_bytes_sent = 0; + int64_t sum_bytes_received = 0; + for (ConnectionInfo& info : ice_transport_stats.connection_infos) { + sum_bytes_sent += info.sent_total_bytes; + sum_bytes_received += info.recv_total_bytes; + } + + EXPECT_EQ(10 * 36U, sum_bytes_sent); + EXPECT_EQ(10 * 36U, sum_bytes_received); + + EXPECT_EQ(20 * 36U, ice_transport_stats2.bytes_sent); + EXPECT_EQ(20 * 36U, ice_transport_stats2.bytes_received); + DestroyChannels(); } @@ -2348,8 +2429,8 @@ TEST_F(P2PTransportChannelTest, // acknowledgement in the connectivity check from the remote peer. TEST_F(P2PTransportChannelTest, CanConnectWithPiggybackCheckAcknowledgementWhenCheckResponseBlocked) { - webrtc::test::ScopedFieldTrials field_trials( - "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/"); + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-PiggybackIceCheckAcknowledgement/Enabled/"); rtc::ScopedFakeClock clock; ConfigureEndpoints(OPEN, OPEN, kOnlyLocalPorts, kOnlyLocalPorts); IceConfig ep1_config; @@ -3982,10 +4063,10 @@ TEST_F(P2PTransportChannelPingTest, TestSelectConnectionBeforeNomination) { // that sends a ping directly when a connection has been nominated // i.e on the ICE_CONTROLLED-side. TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_nomination_ice_controlled:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLED); @@ -4022,10 +4103,10 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnNomination) { // that sends a ping directly when switching to a new connection // on the ICE_CONTROLLING-side. TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_switch_ice_controlling:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4059,10 +4140,10 @@ TEST_F(P2PTransportChannelPingTest, TestPingOnSwitch) { // that sends a ping directly when selecteing a new connection // on the ICE_CONTROLLING-side (i.e also initial selection). TEST_F(P2PTransportChannelPingTest, TestPingOnSelected) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/send_ping_on_selected_ice_controlling:true/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("receiving state change", 1, &pa); + P2PTransportChannel ch("receiving state change", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.SetIceRole(ICEROLE_CONTROLLING); @@ -4806,10 +4887,10 @@ TEST_F(P2PTransportChannelPingTest, TestPortDestroyedAfterTimeoutAndPruned) { } TEST_F(P2PTransportChannelPingTest, TestMaxOutstandingPingsFieldTrial) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/max_outstanding_pings:3/"); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("max", 1, &pa); + P2PTransportChannel ch("max", 1, &pa, &field_trials); ch.SetIceConfig(ch.config()); PrepareChannel(&ch); ch.MaybeStartGathering(); @@ -4847,8 +4928,10 @@ class P2PTransportChannelMostLikelyToWorkFirstTest P2PTransportChannel& StartTransportChannel( bool prioritize_most_likely_to_work, - int stable_writable_connection_ping_interval) { - channel_.reset(new P2PTransportChannel("checks", 1, allocator())); + int stable_writable_connection_ping_interval, + const webrtc::FieldTrialsView* field_trials = nullptr) { + channel_.reset( + new P2PTransportChannel("checks", 1, allocator(), field_trials)); IceConfig config = channel_->config(); config.prioritize_most_likely_candidate_pairs = prioritize_most_likely_to_work; @@ -5015,9 +5098,9 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, // I.e that we never create connection between relay and non-relay. TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestSkipRelayToNonRelayConnectionsFieldTrial) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"); - P2PTransportChannel& ch = StartTransportChannel(true, 500); + P2PTransportChannel& ch = StartTransportChannel(true, 500, &field_trials); EXPECT_TRUE_WAIT(ch.ports().size() == 2, kDefaultTimeout); EXPECT_EQ(ch.ports()[0]->Type(), LOCAL_PORT_TYPE); EXPECT_EQ(ch.ports()[1]->Type(), RELAY_PORT_TYPE); @@ -5068,8 +5151,10 @@ TEST_F(P2PTransportChannelMostLikelyToWorkFirstTest, TestTcpTurn) { TEST(P2PTransportChannelResolverTest, HostnameCandidateIsResolved) { ResolverFactoryFixture resolver_fixture; FakePortAllocator allocator(rtc::Thread::Current(), nullptr); - auto channel = - P2PTransportChannel::Create("tn", 0, &allocator, &resolver_fixture); + webrtc::IceTransportInit init; + init.set_port_allocator(&allocator); + init.set_async_dns_resolver_factory(&resolver_fixture); + auto channel = P2PTransportChannel::Create("tn", 0, std::move(init)); Candidate hostname_candidate; SocketAddress hostname_address("fake.test", 1000); hostname_candidate.set_address(hostname_address); @@ -5831,7 +5916,8 @@ TEST_F(P2PTransportChannelTest, // i.e surface_ice_candidates_on_ice_transport_type_changed requires // coordination outside of webrtc to function properly. TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( + field_trials_, "WebRTC-IceFieldTrials/skip_relay_to_non_relay_connections:true/"); rtc::ScopedFakeClock clock; @@ -5895,7 +5981,7 @@ TEST_F(P2PTransportChannelTest, SurfaceRequiresCoordination) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening:0/"); constexpr int kMargin = 10; @@ -5903,7 +5989,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5919,7 +6005,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening0) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening:100/"); constexpr int kMargin = 10; @@ -5927,7 +6013,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5943,7 +6029,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampening) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/initial_select_dampening_ping_received:100/"); constexpr int kMargin = 10; @@ -5951,7 +6037,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5968,7 +6054,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningPingReceived) { } TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningBoth) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-IceFieldTrials/" "initial_select_dampening:100,initial_select_dampening_ping_received:" "50/"); @@ -5978,7 +6064,7 @@ TEST_F(P2PTransportChannelPingTest, TestInitialSelectDampeningBoth) { clock.AdvanceTime(webrtc::TimeDelta::Seconds(1)); FakePortAllocator pa(rtc::Thread::Current(), nullptr); - P2PTransportChannel ch("test channel", 1, &pa); + P2PTransportChannel ch("test channel", 1, &pa, &field_trials); PrepareChannel(&ch); ch.SetIceConfig(ch.config()); ch.MaybeStartGathering(); @@ -5999,11 +6085,12 @@ TEST(P2PTransportChannel, InjectIceController) { MockIceControllerFactory factory; FakePortAllocator pa(rtc::Thread::Current(), nullptr); EXPECT_CALL(factory, RecordIceControllerCreated()).Times(1); - auto dummy = std::make_unique( - "transport_name", - /* component= */ 77, &pa, - /* async_resolver_factory = */ nullptr, - /* event_log = */ nullptr, &factory); + webrtc::IceTransportInit init; + init.set_port_allocator(&pa); + init.set_ice_controller_factory(&factory); + auto dummy = + P2PTransportChannel::Create("transport_name", + /* component= */ 77, std::move(init)); } class ForgetLearnedStateController : public cricket::BasicIceController { @@ -6052,8 +6139,12 @@ class ForgetLearnedStateControllerFactory TEST_F(P2PTransportChannelPingTest, TestForgetLearnedState) { ForgetLearnedStateControllerFactory factory; FakePortAllocator pa(rtc::Thread::Current(), nullptr); - auto ch = P2PTransportChannel::Create("ping sufficiently", 1, &pa, nullptr, - nullptr, &factory); + webrtc::IceTransportInit init; + init.set_port_allocator(&pa); + init.set_ice_controller_factory(&factory); + auto ch = + P2PTransportChannel::Create("ping sufficiently", 1, std::move(init)); + PrepareChannel(ch.get()); ch->MaybeStartGathering(); ch->AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "1.1.1.1", 1, 1)); @@ -6170,7 +6261,7 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { const std::string field_trial = std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - webrtc::test::ScopedFieldTrials field_trials(field_trial); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, field_trial); rtc::ScopedFakeClock clock; // Use local + relay @@ -6216,11 +6307,11 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnected) { clock.AdvanceTime(webrtc::TimeDelta::Millis(10 * delay)); if (stop_gather_on_strongly_connected) { - // The relay candiates gathered has not been propagated to channel. + // The relay candidates gathered has not been propagated to channel. EXPECT_EQ(ep1->saved_candidates_.size(), 0u); EXPECT_EQ(ep2->saved_candidates_.size(), 0u); } else { - // The relay candiates gathered has been propagated to channel. + // The relay candidates gathered has been propagated to channel. EXPECT_EQ(ep1->saved_candidates_.size(), 1u); EXPECT_EQ(ep2->saved_candidates_.size(), 1u); } @@ -6231,7 +6322,7 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { const std::string field_trial = std::string("WebRTC-IceFieldTrials/stop_gather_on_strongly_connected:") + (stop_gather_on_strongly_connected ? "true/" : "false/"); - webrtc::test::ScopedFieldTrials field_trials(field_trial); + webrtc::test::ScopedKeyValueConfig field_trials(field_trials_, field_trial); rtc::ScopedFakeClock clock; // Use local + relay @@ -6278,11 +6369,11 @@ TEST_P(GatherAfterConnectedTest, GatherAfterConnectedMultiHomed) { clock.AdvanceTime(webrtc::TimeDelta::Millis(10 * delay)); if (stop_gather_on_strongly_connected) { - // The relay candiates gathered has not been propagated to channel. + // The relay candidates gathered has not been propagated to channel. EXPECT_EQ(ep1->saved_candidates_.size(), 0u); EXPECT_EQ(ep2->saved_candidates_.size(), 0u); } else { - // The relay candiates gathered has been propagated. + // The relay candidates gathered has been propagated. EXPECT_EQ(ep1->saved_candidates_.size(), 2u); EXPECT_EQ(ep2->saved_candidates_.size(), 1u); } diff --git a/p2p/base/port.cc b/p2p/base/port.cc index c616658fba..3bd09dc024 100644 --- a/p2p/base/port.cc +++ b/p2p/base/port.cc @@ -19,6 +19,7 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "p2p/base/connection.h" #include "p2p/base/port_allocator.h" #include "rtc_base/checks.h" @@ -34,7 +35,6 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -108,9 +108,10 @@ std::string Port::ComputeFoundation(const std::string& type, Port::Port(rtc::Thread* thread, const std::string& type, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, const std::string& username_fragment, - const std::string& password) + const std::string& password, + const webrtc::FieldTrialsView* field_trials) : thread_(thread), factory_(factory), type_(type), @@ -127,7 +128,8 @@ Port::Port(rtc::Thread* thread, ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), shared_socket_(true), - weak_factory_(this) { + weak_factory_(this), + field_trials_(field_trials) { RTC_DCHECK(factory_ != NULL); Construct(); } @@ -135,11 +137,12 @@ Port::Port(rtc::Thread* thread, Port::Port(rtc::Thread* thread, const std::string& type, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username_fragment, - const std::string& password) + const std::string& password, + const webrtc::FieldTrialsView* field_trials) : thread_(thread), factory_(factory), type_(type), @@ -156,7 +159,8 @@ Port::Port(rtc::Thread* thread, ice_role_(ICEROLE_UNKNOWN), tiebreaker_(0), shared_socket_(false), - weak_factory_(this) { + weak_factory_(this), + field_trials_(field_trials) { RTC_DCHECK(factory_ != NULL); Construct(); } @@ -171,7 +175,7 @@ void Port::Construct() { password_ = rtc::CreateRandomString(ICE_PWD_LENGTH); } network_->SignalTypeChanged.connect(this, &Port::OnNetworkTypeChanged); - network_cost_ = network_->GetCost(); + network_cost_ = network_->GetCost(*field_trials_); thread_->PostDelayed(RTC_FROM_HERE, timeout_delay_, this, MSG_DESTROY_IF_DEAD); @@ -203,7 +207,7 @@ Port::~Port() { const std::string& Port::Type() const { return type_; } -rtc::Network* Port::Network() const { +const rtc::Network* Port::Network() const { return network_; } @@ -276,6 +280,7 @@ void Port::AddAddress(const rtc::SocketAddress& address, c.set_tcptype(tcptype); c.set_network_name(network_->name()); c.set_network_type(network_->type()); + c.set_underlying_type_for_vpn(network_->underlying_type_for_vpn()); c.set_url(url); c.set_related_address(related_address); @@ -301,7 +306,7 @@ bool Port::MaybeObfuscateAddress(Candidate* c, auto copy = *c; auto weak_ptr = weak_factory_.GetWeakPtr(); auto callback = [weak_ptr, copy, is_final](const rtc::IPAddress& addr, - const std::string& name) mutable { + absl::string_view name) mutable { RTC_DCHECK(copy.address().ipaddr() == addr); rtc::SocketAddress hostname_address(name, copy.address().port()); // In Port and Connection, we need the IP address information to @@ -352,7 +357,6 @@ void Port::AddOrReplaceConnection(Connection* conn) { ret.first->second = conn; } conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); - SignalConnectionCreated(this, conn); } void Port::OnReadPacket(const char* data, @@ -734,21 +738,21 @@ bool Port::CanHandleIncomingPacketsFrom(const rtc::SocketAddress&) const { return false; } -void Port::SendBindingErrorResponse(StunMessage* request, +void Port::SendBindingErrorResponse(StunMessage* message, const rtc::SocketAddress& addr, int error_code, const std::string& reason) { - RTC_DCHECK(request->type() == STUN_BINDING_REQUEST || - request->type() == GOOG_PING_REQUEST); + RTC_DCHECK(message->type() == STUN_BINDING_REQUEST || + message->type() == GOOG_PING_REQUEST); // Fill in the response message. StunMessage response; - if (request->type() == STUN_BINDING_REQUEST) { + if (message->type() == STUN_BINDING_REQUEST) { response.SetType(STUN_BINDING_ERROR_RESPONSE); } else { response.SetType(GOOG_PING_ERROR_RESPONSE); } - response.SetTransactionID(request->transaction_id()); + response.SetTransactionID(message->transaction_id()); // When doing GICE, we need to write out the error code incorrectly to // maintain backwards compatiblility. @@ -761,15 +765,15 @@ void Port::SendBindingErrorResponse(StunMessage* request, // because we don't have enough information to determine the shared secret. if (error_code != STUN_ERROR_BAD_REQUEST && error_code != STUN_ERROR_UNAUTHORIZED && - request->type() != GOOG_PING_REQUEST) { - if (request->type() == STUN_BINDING_REQUEST) { + message->type() != GOOG_PING_REQUEST) { + if (message->type() == STUN_BINDING_REQUEST) { response.AddMessageIntegrity(password_); } else { response.AddMessageIntegrity32(password_); } } - if (request->type() == STUN_BINDING_REQUEST) { + if (message->type() == STUN_BINDING_REQUEST) { response.AddFingerprint(); } @@ -787,15 +791,15 @@ void Port::SendBindingErrorResponse(StunMessage* request, } void Port::SendUnknownAttributesErrorResponse( - StunMessage* request, + StunMessage* message, const rtc::SocketAddress& addr, const std::vector& unknown_types) { - RTC_DCHECK(request->type() == STUN_BINDING_REQUEST); + RTC_DCHECK(message->type() == STUN_BINDING_REQUEST); // Fill in the response message. StunMessage response; response.SetType(STUN_BINDING_ERROR_RESPONSE); - response.SetTransactionID(request->transaction_id()); + response.SetTransactionID(message->transaction_id()); auto error_attr = StunAttribute::CreateErrorCode(); error_attr->SetCode(STUN_ERROR_UNKNOWN_ATTRIBUTE); @@ -879,7 +883,7 @@ std::string Port::ToString() const { // TODO(honghaiz): Make the network cost configurable from user setting. void Port::UpdateNetworkCost() { - uint16_t new_cost = network_->GetCost(); + uint16_t new_cost = network_->GetCost(*field_trials_); if (network_cost_ == new_cost) { return; } diff --git a/p2p/base/port.h b/p2p/base/port.h index 1ec82f7049..b1dab5e92b 100644 --- a/p2p/base/port.h +++ b/p2p/base/port.h @@ -20,8 +20,10 @@ #include "absl/types/optional.h" #include "api/candidate.h" +#include "api/field_trials_view.h" #include "api/packet_socket_factory.h" #include "api/rtc_error.h" +#include "api/transport/field_trial_based_config.h" #include "api/transport/stun.h" #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h" #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" @@ -35,6 +37,7 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/callback_list.h" #include "rtc_base/checks.h" +#include "rtc_base/memory/always_valid_pointer.h" #include "rtc_base/net_helper.h" #include "rtc_base/network.h" #include "rtc_base/proxy_info.h" @@ -183,17 +186,19 @@ class Port : public PortInterface, Port(rtc::Thread* thread, const std::string& type, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, const std::string& username_fragment, - const std::string& password); + const std::string& password, + const webrtc::FieldTrialsView* field_trials = nullptr); Port(rtc::Thread* thread, const std::string& type, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username_fragment, - const std::string& password); + const std::string& password, + const webrtc::FieldTrialsView* field_trials = nullptr); ~Port() override; // Note that the port type does NOT uniquely identify different subclasses of @@ -202,7 +207,7 @@ class Port : public PortInterface, // conflit in the value of the 2-tuple, make sure that the implementation that // relies on this 2-tuple for RTTI is properly changed. const std::string& Type() const override; - rtc::Network* Network() const override; + const rtc::Network* Network() const override; // Methods to set/get ICE role and tiebreaker values. IceRole GetIceRole() const override; @@ -288,9 +293,6 @@ class Port : public PortInterface, // Returns the connection to the given address or NULL if none exists. Connection* GetConnection(const rtc::SocketAddress& remote_addr) override; - // Called each time a connection is created. - sigslot::signal2 SignalConnectionCreated; - // In a shared socket mode each port which shares the socket will decide // to accept the packet based on the `remote_addr`. Currently only UDP // port implemented this method. @@ -307,12 +309,12 @@ class Port : public PortInterface, const rtc::SocketAddress& remote_addr) const; // Sends a response error to the given request. - void SendBindingErrorResponse(StunMessage* request, + void SendBindingErrorResponse(StunMessage* message, const rtc::SocketAddress& addr, int error_code, const std::string& reason) override; void SendUnknownAttributesErrorResponse( - StunMessage* request, + StunMessage* message, const rtc::SocketAddress& addr, const std::vector& unknown_types); @@ -386,6 +388,8 @@ class Port : public PortInterface, void set_type(const std::string& type) { type_ = type; } + rtc::WeakPtr NewWeakPtr() { return weak_factory_.GetWeakPtr(); } + void AddAddress(const rtc::SocketAddress& address, const rtc::SocketAddress& base_address, const rtc::SocketAddress& related_address, @@ -457,7 +461,7 @@ class Port : public PortInterface, rtc::PacketSocketFactory* const factory_; std::string type_; bool send_retransmit_count_attribute_; - rtc::Network* network_; + const rtc::Network* network_; uint16_t min_port_; uint16_t max_port_; std::string content_name_; @@ -494,6 +498,9 @@ class Port : public PortInterface, MdnsNameRegistrationStatus::kNotStarted; rtc::WeakPtrFactory weak_factory_; + webrtc::AlwaysValidPointer + field_trials_; bool MaybeObfuscateAddress(Candidate* c, const std::string& type, diff --git a/p2p/base/port_interface.h b/p2p/base/port_interface.h index 73c8e36c78..babe27d85d 100644 --- a/p2p/base/port_interface.h +++ b/p2p/base/port_interface.h @@ -49,7 +49,7 @@ class PortInterface { virtual ~PortInterface(); virtual const std::string& Type() const = 0; - virtual rtc::Network* Network() const = 0; + virtual const rtc::Network* Network() const = 0; // Methods to set/get ICE role and tiebreaker values. virtual void SetIceRole(IceRole role) = 0; @@ -107,7 +107,7 @@ class PortInterface { // Sends a response message (normal or error) to the given request. One of // these methods should be called as a response to SignalUnknownAddress. - virtual void SendBindingErrorResponse(StunMessage* request, + virtual void SendBindingErrorResponse(StunMessage* message, const rtc::SocketAddress& addr, int error_code, const std::string& reason) = 0; diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc index 6ab40a66ad..1c73c96407 100644 --- a/p2p/base/port_unittest.cc +++ b/p2p/base/port_unittest.cc @@ -63,8 +63,8 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::AsyncListenSocket; using rtc::AsyncPacketSocket; @@ -137,7 +137,7 @@ class TestPort : public Port { TestPort(rtc::Thread* thread, const std::string& type, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username_fragment, @@ -200,7 +200,7 @@ class TestPort : public Port { virtual Connection* CreateConnection(const Candidate& remote_candidate, CandidateOrigin origin) { - Connection* conn = new ProxyConnection(this, 0, remote_candidate); + Connection* conn = new ProxyConnection(NewWeakPtr(), 0, remote_candidate); AddOrReplaceConnection(conn); // Set use-candidate attribute flag as this will add USE-CANDIDATE attribute // in STUN binding requests. @@ -520,7 +520,8 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr CreateUdpPort(const SocketAddress& addr, PacketSocketFactory* socket_factory) { return UDPPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0, - username_, password_, true, absl::nullopt); + username_, password_, true, absl::nullopt, + &field_trials_); } std::unique_ptr CreateTcpPort(const SocketAddress& addr) { return CreateTcpPort(addr, &socket_factory_); @@ -528,14 +529,15 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr CreateTcpPort(const SocketAddress& addr, PacketSocketFactory* socket_factory) { return TCPPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0, - username_, password_, true); + username_, password_, true, &field_trials_); } std::unique_ptr CreateStunPort(const SocketAddress& addr, rtc::PacketSocketFactory* factory) { ServerAddresses stun_servers; stun_servers.insert(kStunAddr); return StunPort::Create(&main_, factory, MakeNetwork(addr), 0, 0, username_, - password_, stun_servers, absl::nullopt); + password_, stun_servers, absl::nullopt, + &field_trials_); } std::unique_ptr CreateRelayPort(const SocketAddress& addr, ProtocolType int_proto, @@ -557,11 +559,22 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { ProtocolType int_proto, ProtocolType ext_proto, const rtc::SocketAddress& server_addr) { - return TurnPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0, - username_, password_, - ProtocolAddress(server_addr, int_proto), - kRelayCredentials, 0, {}, {}, nullptr, nullptr); + RelayServerConfig config; + config.credentials = kRelayCredentials; + ProtocolAddress server_address(server_addr, int_proto); + CreateRelayPortArgs args; + args.network_thread = &main_; + args.socket_factory = socket_factory; + args.network = MakeNetwork(addr); + args.username = username_; + args.password = password_; + args.server_address = &server_address; + args.config = &config; + args.field_trials = &field_trials_; + + return TurnPort::Create(args, 0, 0); } + std::unique_ptr CreateNatServer(const SocketAddress& addr, rtc::NATType type) { return std::make_unique(type, ss_.get(), addr, addr, @@ -659,8 +672,8 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { // Ensure redundant SignalClose events on TcpConnection won't break tcp // reconnection. Chromium will fire SignalClose for all outstanding IPC // packets during reconnection. - tcp_conn1->socket()->SignalClose(tcp_conn1->socket(), 0); - tcp_conn2->socket()->SignalClose(tcp_conn2->socket(), 0); + tcp_conn1->socket()->NotifyClosedForTest(0); + tcp_conn2->socket()->NotifyClosedForTest(0); // Speed up destroying ch2's connection such that the test is ready to // accept a new connection from ch1 before ch1's connection destroys itself. @@ -777,7 +790,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { return port; } // Overload to create a test port given an rtc::Network directly. - std::unique_ptr CreateTestPort(rtc::Network* network, + std::unique_ptr CreateTestPort(const rtc::Network* network, const std::string& username, const std::string& password) { auto port = std::make_unique(&main_, "test", &socket_factory_, @@ -823,6 +836,7 @@ class PortTest : public ::testing::Test, public sigslot::has_slots<> { std::string password_; bool role_conflict_; int ports_destroyed_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; void PortTest::TestConnectivity(const char* name1, @@ -1611,7 +1625,7 @@ TEST_F(PortTest, TestDisableInterfaceOfTcpPort) { lconn->Ping(0); // Now disconnect the client socket... - socket->SignalClose(socket, 1); + socket->NotifyClosedForTest(1); // And prevent new sockets from being created. socket_factory.set_next_client_tcp_socket(nullptr); @@ -2097,7 +2111,7 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { rport->SetIceTiebreaker(kTiebreaker2); uint16_t lnetwork_id = 9; - lport->Network()->set_id(lnetwork_id); + test_network->set_id(lnetwork_id); // Send a fake ping from lport to rport. lport->PrepareAddress(); rport->PrepareAddress(); @@ -2118,7 +2132,7 @@ TEST_F(PortTest, TestNetworkInfoAttribute) { // Send a fake ping from rport to lport. test_network->set_type(rtc::ADAPTER_TYPE_CELLULAR); uint16_t rnetwork_id = 8; - rport->Network()->set_id(rnetwork_id); + test_network->set_id(rnetwork_id); Connection* rconn = rport->CreateConnection(lport->Candidates()[0], Port::ORIGIN_MESSAGE); rconn->Ping(0); @@ -2627,7 +2641,7 @@ TEST_F(PortTest, TestCandidateFoundation) { } // This test verifies the related addresses of different types of -// ICE candiates. +// ICE candidates. TEST_F(PortTest, TestCandidateRelatedAddress) { auto nat_server = CreateNatServer(kNatAddr1, NAT_OPEN_CONE); auto udpport = CreateUdpPort(kLocalAddr1); diff --git a/p2p/base/stun_port.cc b/p2p/base/stun_port.cc index 1ba6486049..e846194a4c 100644 --- a/p2p/base/stun_port.cc +++ b/p2p/base/stun_port.cc @@ -40,12 +40,15 @@ class StunBindingRequest : public StunRequest { StunBindingRequest(UDPPort* port, const rtc::SocketAddress& addr, int64_t start_time) - : port_(port), server_addr_(addr), start_time_(start_time) {} + : StunRequest(port->request_manager()), + port_(port), + server_addr_(addr), + start_time_(start_time) {} const rtc::SocketAddress& server_addr() const { return server_addr_; } - void Prepare(StunMessage* request) override { - request->SetType(STUN_BINDING_REQUEST); + void Prepare(StunMessage* message) override { + message->SetType(STUN_BINDING_REQUEST); } void OnResponse(StunMessage* response) override { @@ -63,7 +66,7 @@ class StunBindingRequest : public StunRequest { // The keep-alive requests will be stopped after its lifetime has passed. if (WithinLifetime(rtc::TimeMillis())) { - port_->requests_.SendDelayed( + port_->request_manager_.SendDelayed( new StunBindingRequest(port_, server_addr_, start_time_), port_->stun_keepalive_delay()); } @@ -88,7 +91,7 @@ class StunBindingRequest : public StunRequest { int64_t now = rtc::TimeMillis(); if (WithinLifetime(now) && rtc::TimeDiff(now, start_time_) < RETRY_TIMEOUT) { - port_->requests_.SendDelayed( + port_->request_manager_.SendDelayed( new StunBindingRequest(port_, server_addr_, start_time_), port_->stun_keepalive_delay()); } @@ -153,13 +156,24 @@ bool UDPPort::AddressResolver::GetResolvedAddress( UDPPort::UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, - bool emit_local_for_anyaddress) - : Port(thread, LOCAL_PORT_TYPE, factory, network, username, password), - requests_(thread), + bool emit_local_for_anyaddress, + const webrtc::FieldTrialsView* field_trials) + : Port(thread, + LOCAL_PORT_TYPE, + factory, + network, + username, + password, + field_trials), + request_manager_( + thread, + [this](const void* data, size_t size, StunRequest* request) { + OnSendPacket(data, size, request); + }), socket_(socket), error_(0), ready_(false), @@ -169,12 +183,13 @@ UDPPort::UDPPort(rtc::Thread* thread, UDPPort::UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool emit_local_for_anyaddress) + bool emit_local_for_anyaddress, + const webrtc::FieldTrialsView* field_trials) : Port(thread, LOCAL_PORT_TYPE, factory, @@ -182,8 +197,13 @@ UDPPort::UDPPort(rtc::Thread* thread, min_port, max_port, username, - password), - requests_(thread), + password, + field_trials), + request_manager_( + thread, + [this](const void* data, size_t size, StunRequest* request) { + OnSendPacket(data, size, request); + }), socket_(nullptr), error_(0), ready_(false), @@ -206,7 +226,6 @@ bool UDPPort::Init() { socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket); socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend); socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady); - requests_.SignalSendPacket.connect(this, &UDPPort::OnSendPacket); return true; } @@ -216,7 +235,7 @@ UDPPort::~UDPPort() { } void UDPPort::PrepareAddress() { - RTC_DCHECK(requests_.empty()); + RTC_DCHECK(request_manager_.empty()); if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) { OnLocalAddressReady(socket_, socket_->GetLocalAddress()); } @@ -266,7 +285,7 @@ Connection* UDPPort::CreateConnection(const Candidate& address, mdns_name_registration_status() != MdnsNameRegistrationStatus::kNotStarted); - Connection* conn = new ProxyConnection(this, 0, address); + Connection* conn = new ProxyConnection(NewWeakPtr(), 0, address); AddOrReplaceConnection(conn); return conn; } @@ -381,7 +400,7 @@ void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket, // will eat it because it might be a response to a retransmitted packet, and // we already cleared the request when we got the first response. if (server_addresses_.find(remote_addr) != server_addresses_.end()) { - requests_.CheckResponse(data, size); + request_manager_.CheckResponse(data, size); return; } @@ -404,7 +423,7 @@ void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) { void UDPPort::SendStunBindingRequests() { // We will keep pinging the stun server to make sure our NAT pin-hole stays // open until the deadline (specified in SendStunBindingRequest). - RTC_DCHECK(requests_.empty()); + RTC_DCHECK(request_manager_.empty()); for (ServerAddresses::const_iterator it = server_addresses_.begin(); it != server_addresses_.end(); ++it) { @@ -454,7 +473,7 @@ void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) { } else if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) { // Check if `server_addr_` is compatible with the port's ip. if (IsCompatibleAddress(stun_addr)) { - requests_.Send( + request_manager_.Send( new StunBindingRequest(this, stun_addr, rtc::TimeMillis())); } else { // Since we can't send stun messages to the server, we should mark this @@ -599,17 +618,18 @@ bool UDPPort::HasCandidateWithAddress(const rtc::SocketAddress& addr) const { std::unique_ptr StunPort::Create( rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, const ServerAddresses& servers, - absl::optional stun_keepalive_interval) { + absl::optional stun_keepalive_interval, + const webrtc::FieldTrialsView* field_trials) { // Using `new` to access a non-public constructor. - auto port = - absl::WrapUnique(new StunPort(thread, factory, network, min_port, - max_port, username, password, servers)); + auto port = absl::WrapUnique(new StunPort(thread, factory, network, min_port, + max_port, username, password, + servers, field_trials)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -619,12 +639,13 @@ std::unique_ptr StunPort::Create( StunPort::StunPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - const ServerAddresses& servers) + const ServerAddresses& servers, + const webrtc::FieldTrialsView* field_trials) : UDPPort(thread, factory, network, @@ -632,7 +653,8 @@ StunPort::StunPort(rtc::Thread* thread, max_port, username, password, - false) { + false, + field_trials) { // UDPPort will set these to local udp, updating these to STUN. set_type(STUN_PORT_TYPE); set_server_addresses(servers); diff --git a/p2p/base/stun_port.h b/p2p/base/stun_port.h index 394c1336e2..3968c17a26 100644 --- a/p2p/base/stun_port.h +++ b/p2p/base/stun_port.h @@ -35,16 +35,17 @@ class UDPPort : public Port { static std::unique_ptr Create( rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, bool emit_local_for_anyaddress, - absl::optional stun_keepalive_interval) { + absl::optional stun_keepalive_interval, + const webrtc::FieldTrialsView* field_trials = nullptr) { // Using `new` to access a non-public constructor. - auto port = - absl::WrapUnique(new UDPPort(thread, factory, network, socket, username, - password, emit_local_for_anyaddress)); + auto port = absl::WrapUnique( + new UDPPort(thread, factory, network, socket, username, password, + emit_local_for_anyaddress, field_trials)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -55,17 +56,18 @@ class UDPPort : public Port { static std::unique_ptr Create( rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, bool emit_local_for_anyaddress, - absl::optional stun_keepalive_interval) { + absl::optional stun_keepalive_interval, + const webrtc::FieldTrialsView* field_trials = nullptr) { // Using `new` to access a non-public constructor. - auto port = absl::WrapUnique(new UDPPort(thread, factory, network, min_port, - max_port, username, password, - emit_local_for_anyaddress)); + auto port = absl::WrapUnique( + new UDPPort(thread, factory, network, min_port, max_port, username, + password, emit_local_for_anyaddress, field_trials)); port->set_stun_keepalive_delay(stun_keepalive_interval); if (!port->Init()) { return nullptr; @@ -112,27 +114,31 @@ class UDPPort : public Port { stun_keepalive_lifetime_ = lifetime; } // Returns true if there is a pending request with type `msg_type`. - bool HasPendingRequest(int msg_type) { - return requests_.HasRequest(msg_type); + bool HasPendingRequestForTest(int msg_type) { + return request_manager_.HasRequestForTest(msg_type); } + StunRequestManager& request_manager() { return request_manager_; } + protected: UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool emit_local_for_anyaddress); + bool emit_local_for_anyaddress, + const webrtc::FieldTrialsView* field_trials); UDPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, - bool emit_local_for_anyaddress); + bool emit_local_for_anyaddress, + const webrtc::FieldTrialsView* field_trials); bool Init(); @@ -240,7 +246,7 @@ class UDPPort : public Port { ServerAddresses server_addresses_; ServerAddresses bind_request_succeeded_servers_; ServerAddresses bind_request_failed_servers_; - StunRequestManager requests_; + StunRequestManager request_manager_; rtc::AsyncPacketSocket* socket_; int error_; int send_error_count_ = 0; @@ -264,25 +270,27 @@ class StunPort : public UDPPort { static std::unique_ptr Create( rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, const ServerAddresses& servers, - absl::optional stun_keepalive_interval); + absl::optional stun_keepalive_interval, + const webrtc::FieldTrialsView* field_trials); void PrepareAddress() override; protected: StunPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - const ServerAddresses& servers); + const ServerAddresses& servers, + const webrtc::FieldTrialsView* field_trials); }; } // namespace cricket diff --git a/p2p/base/stun_port_unittest.cc b/p2p/base/stun_port_unittest.cc index d483bafd84..fa51ed6666 100644 --- a/p2p/base/stun_port_unittest.cc +++ b/p2p/base/stun_port_unittest.cc @@ -20,6 +20,7 @@ #include "rtc_base/ssl_adapter.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" using cricket::ServerAddresses; using rtc::SocketAddress; @@ -77,7 +78,7 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { stun_port_ = cricket::StunPort::Create( rtc::Thread::Current(), &socket_factory_, &network_, 0, 0, rtc::CreateRandomString(16), rtc::CreateRandomString(22), stun_servers, - absl::nullopt); + absl::nullopt, &field_trials_); stun_port_->set_stun_keepalive_delay(stun_keepalive_delay_); // If `stun_keepalive_lifetime_` is negative, let the stun port // choose its lifetime from the network type. @@ -104,7 +105,7 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { stun_port_ = cricket::UDPPort::Create( rtc::Thread::Current(), &socket_factory_, &network_, socket_.get(), rtc::CreateRandomString(16), rtc::CreateRandomString(22), false, - absl::nullopt); + absl::nullopt, &field_trials_); ASSERT_TRUE(stun_port_ != NULL); ServerAddresses stun_servers; stun_servers.insert(server_addr); @@ -175,6 +176,7 @@ class StunPortTestBase : public ::testing::Test, public sigslot::has_slots<> { protected: cricket::IceCandidateErrorEvent error_event_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; class StunPortTestWithRealClock : public StunPortTestBase {}; @@ -391,7 +393,7 @@ TEST_F(StunPortTest, TestStunBindingRequestShortLifetime) { PrepareAddress(); EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock); EXPECT_TRUE_SIMULATED_WAIT( - !port()->HasPendingRequest(cricket::STUN_BINDING_REQUEST), 2000, + !port()->HasPendingRequestForTest(cricket::STUN_BINDING_REQUEST), 2000, fake_clock); } @@ -402,7 +404,7 @@ TEST_F(StunPortTest, TestStunBindingRequestLongLifetime) { PrepareAddress(); EXPECT_TRUE_SIMULATED_WAIT(done(), kTimeoutMs, fake_clock); EXPECT_TRUE_SIMULATED_WAIT( - port()->HasPendingRequest(cricket::STUN_BINDING_REQUEST), 1000, + port()->HasPendingRequestForTest(cricket::STUN_BINDING_REQUEST), 1000, fake_clock); } diff --git a/p2p/base/stun_request.cc b/p2p/base/stun_request.cc index 09a7a8345e..79cd61841e 100644 --- a/p2p/base/stun_request.cc +++ b/p2p/base/stun_request.cc @@ -12,14 +12,15 @@ #include #include +#include #include +#include "absl/memory/memory.h" #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/string_encode.h" #include "rtc_base/time_utils.h" // For TimeMillis -#include "system_wrappers/include/field_trial.h" namespace cricket { @@ -42,45 +43,45 @@ const int STUN_MAX_RETRANSMISSIONS = 8; // Total sends: 9 // work well. const int STUN_MAX_RTO = 8000; // milliseconds, or 5 doublings -StunRequestManager::StunRequestManager(rtc::Thread* thread) : thread_(thread) {} +StunRequestManager::StunRequestManager( + rtc::Thread* thread, + std::function send_packet) + : thread_(thread), send_packet_(std::move(send_packet)) {} -StunRequestManager::~StunRequestManager() { - while (requests_.begin() != requests_.end()) { - StunRequest* request = requests_.begin()->second; - requests_.erase(requests_.begin()); - delete request; - } -} +StunRequestManager::~StunRequestManager() = default; void StunRequestManager::Send(StunRequest* request) { SendDelayed(request, 0); } void StunRequestManager::SendDelayed(StunRequest* request, int delay) { - request->set_manager(this); - RTC_DCHECK(requests_.find(request->id()) == requests_.end()); + RTC_DCHECK_RUN_ON(thread_); + RTC_DCHECK_EQ(this, request->manager()); request->Construct(); - requests_[request->id()] = request; + auto [iter, was_inserted] = + requests_.emplace(request->id(), absl::WrapUnique(request)); + RTC_DCHECK(was_inserted); if (delay > 0) { - thread_->PostDelayed(RTC_FROM_HERE, delay, request, MSG_STUN_SEND, NULL); + thread_->PostDelayed(RTC_FROM_HERE, delay, iter->second.get(), + MSG_STUN_SEND, NULL); } else { - thread_->Send(RTC_FROM_HERE, request, MSG_STUN_SEND, NULL); + thread_->Send(RTC_FROM_HERE, iter->second.get(), MSG_STUN_SEND, NULL); } } -void StunRequestManager::Flush(int msg_type) { - for (const auto& kv : requests_) { - StunRequest* request = kv.second; +void StunRequestManager::FlushForTest(int msg_type) { + RTC_DCHECK_RUN_ON(thread_); + for (const auto& [unused, request] : requests_) { if (msg_type == kAllRequests || msg_type == request->type()) { - thread_->Clear(request, MSG_STUN_SEND); - thread_->Send(RTC_FROM_HERE, request, MSG_STUN_SEND, NULL); + thread_->Clear(request.get(), MSG_STUN_SEND); + thread_->Send(RTC_FROM_HERE, request.get(), MSG_STUN_SEND, NULL); } } } -bool StunRequestManager::HasRequest(int msg_type) { - for (const auto& kv : requests_) { - StunRequest* request = kv.second; +bool StunRequestManager::HasRequestForTest(int msg_type) { + RTC_DCHECK_RUN_ON(thread_); + for (const auto& [unused, request] : requests_) { if (msg_type == kAllRequests || msg_type == request->type()) { return true; } @@ -88,29 +89,13 @@ bool StunRequestManager::HasRequest(int msg_type) { return false; } -void StunRequestManager::Remove(StunRequest* request) { - RTC_DCHECK(request->manager() == this); - RequestMap::iterator iter = requests_.find(request->id()); - if (iter != requests_.end()) { - RTC_DCHECK(iter->second == request); - requests_.erase(iter); - thread_->Clear(request); - } -} - void StunRequestManager::Clear() { - std::vector requests; - for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i) - requests.push_back(i->second); - - for (uint32_t i = 0; i < requests.size(); ++i) { - // StunRequest destructor calls Remove() which deletes requests - // from `requests_`. - delete requests[i]; - } + RTC_DCHECK_RUN_ON(thread_); + requests_.clear(); } bool StunRequestManager::CheckResponse(StunMessage* msg) { + RTC_DCHECK_RUN_ON(thread_); RequestMap::iterator iter = requests_.find(msg->transaction_id()); if (iter == requests_.end()) { // TODO(pthatcher): Log unknown responses without being too spammy @@ -118,7 +103,7 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) { return false; } - StunRequest* request = iter->second; + StunRequest* request = iter->second.get(); // Now that we know the request, we can see if the response is // integrity-protected or not. @@ -131,14 +116,15 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) { msg->ValidateMessageIntegrity(request->msg()->password()); } + bool success = true; + if (!msg->GetNonComprehendedAttributes().empty()) { // If a response contains unknown comprehension-required attributes, it's // simply discarded and the transaction is considered failed. See RFC5389 // sections 7.3.3 and 7.3.4. RTC_LOG(LS_ERROR) << ": Discarding response due to unknown " "comprehension-required attribute."; - delete request; - return false; + success = false; } else if (msg->type() == GetStunSuccessResponseType(request->type())) { if (!msg->IntegrityOk() && !skip_integrity_checking) { return false; @@ -153,11 +139,17 @@ bool StunRequestManager::CheckResponse(StunMessage* msg) { return false; } - delete request; - return true; + requests_.erase(iter); + return success; +} + +bool StunRequestManager::empty() const { + RTC_DCHECK_RUN_ON(thread_); + return requests_.empty(); } bool StunRequestManager::CheckResponse(const char* data, size_t size) { + RTC_DCHECK_RUN_ON(thread_); // Check the appropriate bytes of the stream to see if they match the // transaction ID of a response we are expecting. @@ -187,32 +179,44 @@ bool StunRequestManager::CheckResponse(const char* data, size_t size) { return CheckResponse(response.get()); } -StunRequest::StunRequest() - : count_(0), - timeout_(false), - manager_(0), +void StunRequestManager::OnRequestTimedOut(StunRequest* request) { + RTC_DCHECK_RUN_ON(thread_); + requests_.erase(request->id()); +} + +void StunRequestManager::SendPacket(const void* data, + size_t size, + StunRequest* request) { + RTC_DCHECK_EQ(this, request->manager()); + send_packet_(data, size, request); +} + +StunRequest::StunRequest(StunRequestManager& manager) + : manager_(manager), msg_(new StunMessage()), - tstamp_(0) { + tstamp_(0), + count_(0), + timeout_(false) { msg_->SetTransactionID(rtc::CreateRandomString(kStunTransactionIdLength)); } -StunRequest::StunRequest(StunMessage* request) - : count_(0), timeout_(false), manager_(0), msg_(request), tstamp_(0) { +StunRequest::StunRequest(StunRequestManager& manager, + std::unique_ptr message) + : manager_(manager), + msg_(std::move(message)), + tstamp_(0), + count_(0), + timeout_(false) { msg_->SetTransactionID(rtc::CreateRandomString(kStunTransactionIdLength)); } StunRequest::~StunRequest() { - RTC_DCHECK(manager_ != NULL); - if (manager_) { - manager_->Remove(this); - manager_->thread_->Clear(this); - } - delete msg_; + manager_.network_thread()->Clear(this); } void StunRequest::Construct() { if (msg_->type() == 0) { - Prepare(msg_); + Prepare(msg_.get()); RTC_DCHECK(msg_->type() != 0); } } @@ -223,29 +227,21 @@ int StunRequest::type() { } const StunMessage* StunRequest::msg() const { - return msg_; -} - -StunMessage* StunRequest::mutable_msg() { - return msg_; + return msg_.get(); } int StunRequest::Elapsed() const { + RTC_DCHECK_RUN_ON(network_thread()); return static_cast(rtc::TimeMillis() - tstamp_); } -void StunRequest::set_manager(StunRequestManager* manager) { - RTC_DCHECK(!manager_); - manager_ = manager; -} - void StunRequest::OnMessage(rtc::Message* pmsg) { - RTC_DCHECK(manager_ != NULL); + RTC_DCHECK_RUN_ON(network_thread()); RTC_DCHECK(pmsg->message_id == MSG_STUN_SEND); if (timeout_) { OnTimeout(); - delete this; + manager_.OnRequestTimedOut(this); return; } @@ -253,24 +249,26 @@ void StunRequest::OnMessage(rtc::Message* pmsg) { rtc::ByteBufferWriter buf; msg_->Write(&buf); - manager_->SignalSendPacket(buf.Data(), buf.Length(), this); + manager_.SendPacket(buf.Data(), buf.Length(), this); OnSent(); - manager_->thread_->PostDelayed(RTC_FROM_HERE, resend_delay(), this, - MSG_STUN_SEND, NULL); + manager_.network_thread()->PostDelayed(RTC_FROM_HERE, resend_delay(), this, + MSG_STUN_SEND, NULL); } void StunRequest::OnSent() { + RTC_DCHECK_RUN_ON(network_thread()); count_ += 1; int retransmissions = (count_ - 1); if (retransmissions >= STUN_MAX_RETRANSMISSIONS) { timeout_ = true; } - RTC_LOG(LS_VERBOSE) << "Sent STUN request " << count_ - << "; resend delay = " << resend_delay(); + RTC_DLOG(LS_VERBOSE) << "Sent STUN request " << count_ + << "; resend delay = " << resend_delay(); } int StunRequest::resend_delay() { + RTC_DCHECK_RUN_ON(network_thread()); if (count_ == 0) { return 0; } @@ -279,4 +277,9 @@ int StunRequest::resend_delay() { return std::min(rto, STUN_MAX_RTO); } +void StunRequest::set_timed_out() { + RTC_DCHECK_RUN_ON(network_thread()); + timeout_ = true; +} + } // namespace cricket diff --git a/p2p/base/stun_request.h b/p2p/base/stun_request.h index b417c705cd..0d00772b15 100644 --- a/p2p/base/stun_request.h +++ b/p2p/base/stun_request.h @@ -14,12 +14,13 @@ #include #include +#include #include +#include #include #include "api/transport/stun.h" #include "rtc_base/message_handler.h" -#include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" namespace cricket { @@ -37,7 +38,9 @@ const int STUN_TOTAL_TIMEOUT = 39750; // milliseconds // response or determine that the request has timed out. class StunRequestManager { public: - explicit StunRequestManager(rtc::Thread* thread); + StunRequestManager( + rtc::Thread* thread, + std::function send_packet); ~StunRequestManager(); // Starts sending the given request (perhaps after a delay). @@ -47,15 +50,11 @@ class StunRequestManager { // If `msg_type` is kAllRequests, sends all pending requests right away. // Otherwise, sends those that have a matching type right away. // Only for testing. - void Flush(int msg_type); + void FlushForTest(int msg_type); // Returns true if at least one request with `msg_type` is scheduled for // transmission. For testing only. - bool HasRequest(int msg_type); - - // Removes a stun request that was added previously. This will happen - // automatically when a request succeeds, fails, or times out. - void Remove(StunRequest* request); + bool HasRequestForTest(int msg_type); // Removes all stun requests that were added previously. void Clear(); @@ -65,36 +64,41 @@ class StunRequestManager { bool CheckResponse(StunMessage* msg); bool CheckResponse(const char* data, size_t size); - bool empty() { return requests_.empty(); } + // Called from a StunRequest when a timeout occurs. + void OnRequestTimedOut(StunRequest* request); + + bool empty() const; + + // TODO(tommi): Use TaskQueueBase* instead of rtc::Thread. + rtc::Thread* network_thread() const { return thread_; } - // Raised when there are bytes to be sent. - sigslot::signal3 SignalSendPacket; + void SendPacket(const void* data, size_t size, StunRequest* request); private: - typedef std::map RequestMap; + typedef std::map> RequestMap; rtc::Thread* const thread_; - RequestMap requests_; - - friend class StunRequest; + RequestMap requests_ RTC_GUARDED_BY(thread_); + const std::function send_packet_; }; // Represents an individual request to be sent. The STUN message can either be // constructed beforehand or built on demand. class StunRequest : public rtc::MessageHandler { public: - StunRequest(); - explicit StunRequest(StunMessage* request); + explicit StunRequest(StunRequestManager& manager); + StunRequest(StunRequestManager& manager, + std::unique_ptr message); ~StunRequest() override; // Causes our wrapped StunMessage to be Prepared void Construct(); // The manager handling this request (if it has been scheduled for sending). - StunRequestManager* manager() { return manager_; } + StunRequestManager* manager() { return &manager_; } // Returns the transaction ID of this request. - const std::string& id() { return msg_->transaction_id(); } + const std::string& id() const { return msg_->transaction_id(); } // Returns the reduced transaction ID of this request. uint32_t reduced_transaction_id() const { @@ -107,19 +111,15 @@ class StunRequest : public rtc::MessageHandler { // Returns a const pointer to `msg_`. const StunMessage* msg() const; - // Returns a mutable pointer to `msg_`. - StunMessage* mutable_msg(); - // Time elapsed since last send (in ms) int Elapsed() const; protected: - int count_; - bool timeout_; + friend class StunRequestManager; // Fills in a request object to be sent. Note that request's transaction ID // will already be set and cannot be changed. - virtual void Prepare(StunMessage* request) {} + virtual void Prepare(StunMessage* message) {} // Called when the message receives a response or times out. virtual void OnResponse(StunMessage* response) {} @@ -130,17 +130,21 @@ class StunRequest : public rtc::MessageHandler { // Returns the next delay for resends. virtual int resend_delay(); - private: - void set_manager(StunRequestManager* manager); + webrtc::TaskQueueBase* network_thread() const { + return manager_.network_thread(); + } + + void set_timed_out(); + private: // Handles messages for sending and timeout. void OnMessage(rtc::Message* pmsg) override; - StunRequestManager* manager_; - StunMessage* msg_; - int64_t tstamp_; - - friend class StunRequestManager; + StunRequestManager& manager_; + const std::unique_ptr msg_; + int64_t tstamp_ RTC_GUARDED_BY(network_thread()); + int count_ RTC_GUARDED_BY(network_thread()); + bool timeout_ RTC_GUARDED_BY(network_thread()); }; } // namespace cricket diff --git a/p2p/base/stun_request_unittest.cc b/p2p/base/stun_request_unittest.cc index ce573f087d..050df0b1b0 100644 --- a/p2p/base/stun_request_unittest.cc +++ b/p2p/base/stun_request_unittest.cc @@ -10,6 +10,7 @@ #include "p2p/base/stun_request.h" +#include #include #include "rtc_base/fake_clock.h" @@ -19,18 +20,37 @@ #include "test/gtest.h" namespace cricket { +namespace { +std::unique_ptr CreateStunMessage( + StunMessageType type, + const StunMessage* req = nullptr) { + std::unique_ptr msg = std::make_unique(); + msg->SetType(type); + if (req) { + msg->SetTransactionID(req->transaction_id()); + } + return msg; +} + +int TotalDelay(int sends) { + std::vector delays = {0, 250, 750, 1750, 3750, + 7750, 15750, 23750, 31750, 39750}; + return delays[sends]; +} +} // namespace -class StunRequestTest : public ::testing::Test, public sigslot::has_slots<> { +class StunRequestTest : public ::testing::Test { public: StunRequestTest() - : manager_(rtc::Thread::Current()), + : manager_(rtc::Thread::Current(), + [this](const void* data, size_t size, StunRequest* request) { + OnSendPacket(data, size, request); + }), request_count_(0), response_(NULL), success_(false), failure_(false), - timeout_(false) { - manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket); - } + timeout_(false) {} void OnSendPacket(const void* data, size_t size, StunRequest* req) { request_count_++; @@ -47,21 +67,6 @@ class StunRequestTest : public ::testing::Test, public sigslot::has_slots<> { void OnTimeout() { timeout_ = true; } protected: - static StunMessage* CreateStunMessage(StunMessageType type, - StunMessage* req) { - StunMessage* msg = new StunMessage(); - msg->SetType(type); - if (req) { - msg->SetTransactionID(req->transaction_id()); - } - return msg; - } - static int TotalDelay(int sends) { - std::vector delays = {0, 250, 750, 1750, 3750, - 7750, 15750, 23750, 31750, 39750}; - return delays[sends]; - } - StunRequestManager manager_; int request_count_; StunMessage* response_; @@ -73,9 +78,20 @@ class StunRequestTest : public ::testing::Test, public sigslot::has_slots<> { // Forwards results to the test class. class StunRequestThunker : public StunRequest { public: - StunRequestThunker(StunMessage* msg, StunRequestTest* test) - : StunRequest(msg), test_(test) {} - explicit StunRequestThunker(StunRequestTest* test) : test_(test) {} + StunRequestThunker(StunRequestManager& manager, + StunMessageType message_type, + StunRequestTest* test) + : StunRequest(manager, CreateStunMessage(message_type)), test_(test) { + Construct(); // Triggers a call to `Prepare()` which sets the type. + } + StunRequestThunker(StunRequestManager& manager, StunRequestTest* test) + : StunRequest(manager), test_(test) { + Construct(); // Triggers a call to `Prepare()` which sets the type. + } + + std::unique_ptr CreateResponseMessage(StunMessageType type) { + return CreateStunMessage(type, msg()); + } private: virtual void OnResponse(StunMessage* res) { test_->OnResponse(res); } @@ -84,8 +100,8 @@ class StunRequestThunker : public StunRequest { } virtual void OnTimeout() { test_->OnTimeout(); } - virtual void Prepare(StunMessage* request) { - request->SetType(STUN_BINDING_REQUEST); + virtual void Prepare(StunMessage* message) { + message->SetType(STUN_BINDING_REQUEST); } StunRequestTest* test_; @@ -93,127 +109,124 @@ class StunRequestThunker : public StunRequest { // Test handling of a normal binding response. TEST_F(StunRequestTest, TestSuccess) { - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); - - manager_.Send(new StunRequestThunker(req, this)); - StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); - EXPECT_TRUE(manager_.CheckResponse(res)); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = + request->CreateResponseMessage(STUN_BINDING_RESPONSE); + manager_.Send(request); + EXPECT_TRUE(manager_.CheckResponse(res.get())); - EXPECT_TRUE(response_ == res); + EXPECT_TRUE(response_ == res.get()); EXPECT_TRUE(success_); EXPECT_FALSE(failure_); EXPECT_FALSE(timeout_); - delete res; } // Test handling of an error binding response. TEST_F(StunRequestTest, TestError) { - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); - - manager_.Send(new StunRequestThunker(req, this)); - StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req); - EXPECT_TRUE(manager_.CheckResponse(res)); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = + request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE); + manager_.Send(request); + EXPECT_TRUE(manager_.CheckResponse(res.get())); - EXPECT_TRUE(response_ == res); + EXPECT_TRUE(response_ == res.get()); EXPECT_FALSE(success_); EXPECT_TRUE(failure_); EXPECT_FALSE(timeout_); - delete res; } // Test handling of a binding response with the wrong transaction id. TEST_F(StunRequestTest, TestUnexpected) { - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = CreateStunMessage(STUN_BINDING_RESPONSE); - manager_.Send(new StunRequestThunker(req, this)); - StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL); - EXPECT_FALSE(manager_.CheckResponse(res)); + manager_.Send(request); + EXPECT_FALSE(manager_.CheckResponse(res.get())); EXPECT_TRUE(response_ == NULL); EXPECT_FALSE(success_); EXPECT_FALSE(failure_); EXPECT_FALSE(timeout_); - delete res; } // Test that requests are sent at the right times. TEST_F(StunRequestTest, TestBackoff) { rtc::ScopedFakeClock fake_clock; - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = + request->CreateResponseMessage(STUN_BINDING_RESPONSE); int64_t start = rtc::TimeMillis(); - manager_.Send(new StunRequestThunker(req, this)); - StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); + manager_.Send(request); for (int i = 0; i < 9; ++i) { EXPECT_TRUE_SIMULATED_WAIT(request_count_ != i, STUN_TOTAL_TIMEOUT, fake_clock); int64_t elapsed = rtc::TimeMillis() - start; - RTC_LOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed - << " ms"; + RTC_DLOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed + << " ms"; EXPECT_EQ(TotalDelay(i), elapsed); } - EXPECT_TRUE(manager_.CheckResponse(res)); + EXPECT_TRUE(manager_.CheckResponse(res.get())); - EXPECT_TRUE(response_ == res); + EXPECT_TRUE(response_ == res.get()); EXPECT_TRUE(success_); EXPECT_FALSE(failure_); EXPECT_FALSE(timeout_); - delete res; } // Test that we timeout properly if no response is received. TEST_F(StunRequestTest, TestTimeout) { rtc::ScopedFakeClock fake_clock; - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); - StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = + request->CreateResponseMessage(STUN_BINDING_RESPONSE); - manager_.Send(new StunRequestThunker(req, this)); + manager_.Send(request); SIMULATED_WAIT(false, cricket::STUN_TOTAL_TIMEOUT, fake_clock); - EXPECT_FALSE(manager_.CheckResponse(res)); + EXPECT_FALSE(manager_.CheckResponse(res.get())); EXPECT_TRUE(response_ == NULL); EXPECT_FALSE(success_); EXPECT_FALSE(failure_); EXPECT_TRUE(timeout_); - delete res; } // Regression test for specific crash where we receive a response with the // same id as a request that doesn't have an underlying StunMessage yet. TEST_F(StunRequestTest, TestNoEmptyRequest) { - StunRequestThunker* request = new StunRequestThunker(this); + StunRequestThunker* request = new StunRequestThunker(manager_, this); manager_.SendDelayed(request, 100); StunMessage dummy_req; dummy_req.SetTransactionID(request->id()); - StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req); + std::unique_ptr res = + CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req); - EXPECT_TRUE(manager_.CheckResponse(res)); + EXPECT_TRUE(manager_.CheckResponse(res.get())); - EXPECT_TRUE(response_ == res); + EXPECT_TRUE(response_ == res.get()); EXPECT_TRUE(success_); EXPECT_FALSE(failure_); EXPECT_FALSE(timeout_); - delete res; } // If the response contains an attribute in the "comprehension required" range // which is not recognized, the transaction should be considered a failure and // the response should be ignored. TEST_F(StunRequestTest, TestUnrecognizedComprehensionRequiredAttribute) { - StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); + auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this); + std::unique_ptr res = + request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE); - manager_.Send(new StunRequestThunker(req, this)); - StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req); + manager_.Send(request); res->AddAttribute(StunAttribute::CreateUInt32(0x7777)); - EXPECT_FALSE(manager_.CheckResponse(res)); + EXPECT_FALSE(manager_.CheckResponse(res.get())); EXPECT_EQ(nullptr, response_); EXPECT_FALSE(success_); EXPECT_FALSE(failure_); EXPECT_FALSE(timeout_); - delete res; } } // namespace cricket diff --git a/p2p/base/stun_server.cc b/p2p/base/stun_server.cc index 382b787951..25f0634b05 100644 --- a/p2p/base/stun_server.cc +++ b/p2p/base/stun_server.cc @@ -83,15 +83,15 @@ void StunServer::SendResponse(const StunMessage& msg, RTC_LOG_ERR(LS_ERROR) << "sendto"; } -void StunServer::GetStunBindResponse(StunMessage* request, +void StunServer::GetStunBindResponse(StunMessage* message, const rtc::SocketAddress& remote_addr, StunMessage* response) const { response->SetType(STUN_BINDING_RESPONSE); - response->SetTransactionID(request->transaction_id()); + response->SetTransactionID(message->transaction_id()); - // Tell the user the address that we received their request from. + // Tell the user the address that we received their message from. std::unique_ptr mapped_addr; - if (request->IsLegacy()) { + if (message->IsLegacy()) { mapped_addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS); } else { mapped_addr = StunAttribute::CreateXorAddress(STUN_ATTR_XOR_MAPPED_ADDRESS); diff --git a/p2p/base/stun_server.h b/p2p/base/stun_server.h index f2126db191..54d8d9083c 100644 --- a/p2p/base/stun_server.h +++ b/p2p/base/stun_server.h @@ -58,7 +58,7 @@ class StunServer : public sigslot::has_slots<> { void SendResponse(const StunMessage& msg, const rtc::SocketAddress& addr); // A helper method to compose a STUN binding response. - void GetStunBindResponse(StunMessage* request, + void GetStunBindResponse(StunMessage* message, const rtc::SocketAddress& remote_addr, StunMessage* response) const; diff --git a/p2p/base/tcp_port.cc b/p2p/base/tcp_port.cc index 445b0d03a5..2964c8b078 100644 --- a/p2p/base/tcp_port.cc +++ b/p2p/base/tcp_port.cc @@ -68,6 +68,7 @@ #include +#include #include #include "absl/algorithm/container.h" @@ -86,12 +87,13 @@ namespace cricket { TCPPort::TCPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool allow_listen) + bool allow_listen, + const webrtc::FieldTrialsView* field_trials) : Port(thread, LOCAL_PORT_TYPE, factory, @@ -99,7 +101,8 @@ TCPPort::TCPPort(rtc::Thread* thread, min_port, max_port, username, - password), + password, + field_trials), allow_listen_(allow_listen), error_(0) { // TODO(mallinath) - Set preference value as per RFC 6544. @@ -155,11 +158,11 @@ Connection* TCPPort::CreateConnection(const Candidate& address, // so we need to hand off the "read packet" responsibility to // TCPConnection. socket->SignalReadPacket.disconnect(this); - conn = new TCPConnection(this, address, socket); + conn = new TCPConnection(NewWeakPtr(), address, socket); } else { // Outgoing connection, which will create a new socket for which we still // need to connect SignalReadyToSend and SignalSentPacket. - conn = new TCPConnection(this, address); + conn = new TCPConnection(NewWeakPtr(), address); if (conn->socket()) { conn->socket()->SignalReadyToSend.connect(this, &TCPPort::OnReadyToSend); conn->socket()->SignalSentPacket.connect(this, &TCPPort::OnSentPacket); @@ -341,16 +344,17 @@ void TCPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) { // `ice_unwritable_timeout` in IceConfig when determining the writability state. // Replace this constant with the config parameter assuming the default value if // we decide it is also applicable here. -TCPConnection::TCPConnection(TCPPort* port, +TCPConnection::TCPConnection(rtc::WeakPtr tcp_port, const Candidate& candidate, rtc::AsyncPacketSocket* socket) - : Connection(port, 0, candidate), + : Connection(std::move(tcp_port), 0, candidate), socket_(socket), error_(0), outgoing_(socket == NULL), connection_pending_(false), pretending_to_be_writable_(false), reconnection_timeout_(cricket::CONNECTION_WRITE_CONNECT_TIMEOUT) { + RTC_DCHECK_EQ(port()->GetProtocol(), PROTO_TCP); // Needs to be TCPPort. if (outgoing_) { CreateOutgoingTcpSocket(); } else { @@ -358,7 +362,7 @@ TCPConnection::TCPConnection(TCPPort* port, // what's being checked in OnConnect, but just DCHECKing here. RTC_LOG(LS_VERBOSE) << ToString() << ": socket ipaddr: " << socket_->GetLocalAddress().ToSensitiveString() - << ", port() Network:" << port->Network()->ToString(); + << ", port() Network:" << port()->Network()->ToString(); RTC_DCHECK(absl::c_any_of( port_->Network()->GetIPs(), [this](const rtc::InterfaceAddress& addr) { return socket_->GetLocalAddress().ipaddr() == addr; @@ -397,7 +401,7 @@ int TCPConnection::Send(const void* data, } stats_.sent_total_packets++; rtc::PacketOptions modified_options(options); - static_cast(port_)->CopyPortInformationToPacketInfo( + tcp_port()->CopyPortInformationToPacketInfo( &modified_options.info_signaled_after_sent); int sent = socket_->Send(data, size, modified_options); int64_t now = rtc::TimeMillis(); @@ -515,6 +519,7 @@ void TCPConnection::OnClose(rtc::AsyncPacketSocket* socket, int error) { // initial connect() (i.e. `pretending_to_be_writable_` is false) . We have // to manually destroy here as this connection, as never connected, will not // be scheduled for ping to trigger destroy. + socket_->UnsubscribeClose(this); Destroy(); } } @@ -553,6 +558,11 @@ void TCPConnection::CreateOutgoingTcpSocket() { int opts = (remote_candidate().protocol() == SSLTCP_PROTOCOL_NAME) ? rtc::PacketSocketFactory::OPT_TLS_FAKE : 0; + + if (socket_) { + socket_->UnsubscribeClose(this); + } + rtc::PacketSocketTcpOptions tcp_opts; tcp_opts.opts = opts; socket_.reset(port()->socket_factory()->CreateClientTcpSocket( @@ -586,7 +596,8 @@ void TCPConnection::ConnectSocketSignals(rtc::AsyncPacketSocket* socket) { } socket->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket); socket->SignalReadyToSend.connect(this, &TCPConnection::OnReadyToSend); - socket->SignalClose.connect(this, &TCPConnection::OnClose); + socket->SubscribeClose( + this, [this](rtc::AsyncPacketSocket* s, int err) { OnClose(s, err); }); } } // namespace cricket diff --git a/p2p/base/tcp_port.h b/p2p/base/tcp_port.h index 07d483cc3f..6bd935f613 100644 --- a/p2p/base/tcp_port.h +++ b/p2p/base/tcp_port.h @@ -34,18 +34,20 @@ class TCPConnection; // call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection. class TCPPort : public Port { public: - static std::unique_ptr Create(rtc::Thread* thread, - rtc::PacketSocketFactory* factory, - rtc::Network* network, - uint16_t min_port, - uint16_t max_port, - const std::string& username, - const std::string& password, - bool allow_listen) { + static std::unique_ptr Create( + rtc::Thread* thread, + rtc::PacketSocketFactory* factory, + const rtc::Network* network, + uint16_t min_port, + uint16_t max_port, + const std::string& username, + const std::string& password, + bool allow_listen, + const webrtc::FieldTrialsView* field_trials = nullptr) { // Using `new` to access a non-public constructor. return absl::WrapUnique(new TCPPort(thread, factory, network, min_port, max_port, username, password, - allow_listen)); + allow_listen, field_trials)); } ~TCPPort() override; @@ -66,12 +68,13 @@ class TCPPort : public Port { protected: TCPPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, const std::string& password, - bool allow_listen); + bool allow_listen, + const webrtc::FieldTrialsView* field_trials); // Handles sending using the local TCP socket. int SendTo(const void* data, @@ -121,12 +124,12 @@ class TCPPort : public Port { friend class TCPConnection; }; -class TCPConnection : public Connection { +class TCPConnection : public Connection, public sigslot::has_slots<> { public: // Connection is outgoing unless socket is specified - TCPConnection(TCPPort* port, + TCPConnection(rtc::WeakPtr tcp_port, const Candidate& candidate, - rtc::AsyncPacketSocket* socket = 0); + rtc::AsyncPacketSocket* socket = nullptr); ~TCPConnection() override; int Send(const void* data, @@ -166,6 +169,11 @@ class TCPConnection : public Connection { const int64_t& packet_time_us); void OnReadyToSend(rtc::AsyncPacketSocket* socket); + TCPPort* tcp_port() { + RTC_DCHECK_EQ(port()->GetProtocol(), PROTO_TCP); + return static_cast(port()); + } + std::unique_ptr socket_; int error_; bool outgoing_; diff --git a/p2p/base/tcp_port_unittest.cc b/p2p/base/tcp_port_unittest.cc index 9af934f10e..8adf35ca4c 100644 --- a/p2p/base/tcp_port_unittest.cc +++ b/p2p/base/tcp_port_unittest.cc @@ -25,6 +25,7 @@ #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::Connection; using cricket::ICE_PWD_LENGTH; @@ -82,12 +83,13 @@ class TCPPortTest : public ::testing::Test, public sigslot::has_slots<> { std::unique_ptr CreateTCPPort(const SocketAddress& addr) { return std::unique_ptr( TCPPort::Create(&main_, &socket_factory_, MakeNetwork(addr), 0, 0, - username_, password_, true)); + username_, password_, true, &field_trials_)); } - std::unique_ptr CreateTCPPort(rtc::Network* network) { - return std::unique_ptr(TCPPort::Create( - &main_, &socket_factory_, network, 0, 0, username_, password_, true)); + std::unique_ptr CreateTCPPort(const rtc::Network* network) { + return std::unique_ptr( + TCPPort::Create(&main_, &socket_factory_, network, 0, 0, username_, + password_, true, &field_trials_)); } protected: @@ -100,6 +102,7 @@ class TCPPortTest : public ::testing::Test, public sigslot::has_slots<> { rtc::BasicPacketSocketFactory socket_factory_; std::string username_; std::string password_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; TEST_F(TCPPortTest, TestTCPPortWithLocalhostAddress) { diff --git a/p2p/base/transport_description_factory.cc b/p2p/base/transport_description_factory.cc index e46114ed83..7eb21da166 100644 --- a/p2p/base/transport_description_factory.cc +++ b/p2p/base/transport_description_factory.cc @@ -21,8 +21,9 @@ namespace cricket { -TransportDescriptionFactory::TransportDescriptionFactory() - : secure_(SEC_DISABLED) {} +TransportDescriptionFactory::TransportDescriptionFactory( + const webrtc::FieldTrialsView& field_trials) + : secure_(SEC_DISABLED), field_trials_(field_trials) {} TransportDescriptionFactory::~TransportDescriptionFactory() = default; diff --git a/p2p/base/transport_description_factory.h b/p2p/base/transport_description_factory.h index 0be7f32929..b4d8822cd2 100644 --- a/p2p/base/transport_description_factory.h +++ b/p2p/base/transport_description_factory.h @@ -13,6 +13,7 @@ #include +#include "api/field_trials_view.h" #include "p2p/base/ice_credentials_iterator.h" #include "p2p/base/transport_description.h" #include "rtc_base/rtc_certificate.h" @@ -37,7 +38,8 @@ struct TransportOptions { class TransportDescriptionFactory { public: // Default ctor; use methods below to set configuration. - TransportDescriptionFactory(); + explicit TransportDescriptionFactory( + const webrtc::FieldTrialsView& field_trials); ~TransportDescriptionFactory(); SecurePolicy secure() const { return secure_; } @@ -73,12 +75,15 @@ class TransportDescriptionFactory { const TransportDescription* current_description, IceCredentialsIterator* ice_credentials) const; + const webrtc::FieldTrialsView& trials() const { return field_trials_; } + private: bool SetSecurityInfo(TransportDescription* description, ConnectionRole role) const; SecurePolicy secure_; rtc::scoped_refptr certificate_; + const webrtc::FieldTrialsView& field_trials_; }; } // namespace cricket diff --git a/p2p/base/transport_description_factory_unittest.cc b/p2p/base/transport_description_factory_unittest.cc index 77a56eff26..75e6e8ee09 100644 --- a/p2p/base/transport_description_factory_unittest.cc +++ b/p2p/base/transport_description_factory_unittest.cc @@ -25,6 +25,7 @@ #include "rtc_base/ssl_identity.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::TransportDescription; using cricket::TransportDescriptionFactory; @@ -36,6 +37,8 @@ class TransportDescriptionFactoryTest : public ::testing::Test { public: TransportDescriptionFactoryTest() : ice_credentials_({}), + f1_(field_trials_), + f2_(field_trials_), cert1_(rtc::RTCCertificate::Create(std::unique_ptr( new rtc::FakeSSLIdentity("User1")))), cert2_(rtc::RTCCertificate::Create(std::unique_ptr( @@ -156,6 +159,7 @@ class TransportDescriptionFactoryTest : public ::testing::Test { } } + webrtc::test::ScopedKeyValueConfig field_trials_; cricket::IceCredentialsIterator ice_credentials_; TransportDescriptionFactory f1_; TransportDescriptionFactory f2_; diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc index bde5bf603c..4e80d6c555 100644 --- a/p2p/base/turn_port.cc +++ b/p2p/base/turn_port.cc @@ -29,7 +29,6 @@ #include "rtc_base/socket_address.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_utils/to_queued_task.h" -#include "system_wrappers/include/field_trial.h" namespace cricket { @@ -74,7 +73,7 @@ static int GetRelayPreference(cricket::ProtocolType proto) { class TurnAllocateRequest : public StunRequest { public: explicit TurnAllocateRequest(TurnPort* port); - void Prepare(StunMessage* request) override; + void Prepare(StunMessage* message) override; void OnSent() override; void OnResponse(StunMessage* response) override; void OnErrorResponse(StunMessage* response) override; @@ -92,7 +91,7 @@ class TurnAllocateRequest : public StunRequest { class TurnRefreshRequest : public StunRequest { public: explicit TurnRefreshRequest(TurnPort* port); - void Prepare(StunMessage* request) override; + void Prepare(StunMessage* message) override; void OnSent() override; void OnResponse(StunMessage* response) override; void OnErrorResponse(StunMessage* response) override; @@ -111,7 +110,7 @@ class TurnCreatePermissionRequest : public StunRequest, TurnEntry* entry, const rtc::SocketAddress& ext_addr, const std::string& remote_ufrag); - void Prepare(StunMessage* request) override; + void Prepare(StunMessage* message) override; void OnSent() override; void OnResponse(StunMessage* response) override; void OnErrorResponse(StunMessage* response) override; @@ -132,7 +131,7 @@ class TurnChannelBindRequest : public StunRequest, public sigslot::has_slots<> { TurnEntry* entry, int channel_id, const rtc::SocketAddress& ext_addr); - void Prepare(StunMessage* request) override; + void Prepare(StunMessage* message) override; void OnSent() override; void OnResponse(StunMessage* response) override; void OnErrorResponse(StunMessage* response) override; @@ -216,33 +215,48 @@ class TurnEntry : public sigslot::has_slots<> { TurnPort::TurnPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - webrtc::TurnCustomizer* customizer) - : Port(thread, RELAY_PORT_TYPE, factory, network, username, password), + const std::vector& tls_alpn_protocols, + const std::vector& tls_elliptic_curves, + webrtc::TurnCustomizer* customizer, + rtc::SSLCertificateVerifier* tls_cert_verifier, + const webrtc::FieldTrialsView* field_trials) + : Port(thread, + RELAY_PORT_TYPE, + factory, + network, + username, + password, + field_trials), server_address_(server_address), - tls_cert_verifier_(nullptr), + tls_alpn_protocols_(tls_alpn_protocols), + tls_elliptic_curves_(tls_elliptic_curves), + tls_cert_verifier_(tls_cert_verifier), credentials_(credentials), socket_(socket), error_(0), stun_dscp_value_(rtc::DSCP_NO_CHANGE), - request_manager_(thread), + request_manager_( + thread, + [this](const void* data, size_t size, StunRequest* request) { + OnSendStunPacket(data, size, request); + }), next_channel_number_(TURN_CHANNEL_NUMBER_START), state_(STATE_CONNECTING), server_priority_(server_priority), allocate_mismatch_retries_(0), - turn_customizer_(customizer) { - request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); -} + turn_customizer_(customizer), + field_trials_(field_trials) {} TurnPort::TurnPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, @@ -253,7 +267,8 @@ TurnPort::TurnPort(rtc::Thread* thread, const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier) + rtc::SSLCertificateVerifier* tls_cert_verifier, + const webrtc::FieldTrialsView* field_trials) : Port(thread, RELAY_PORT_TYPE, factory, @@ -261,7 +276,8 @@ TurnPort::TurnPort(rtc::Thread* thread, min_port, max_port, username, - password), + password, + field_trials), server_address_(server_address), tls_alpn_protocols_(tls_alpn_protocols), tls_elliptic_curves_(tls_elliptic_curves), @@ -270,14 +286,17 @@ TurnPort::TurnPort(rtc::Thread* thread, socket_(NULL), error_(0), stun_dscp_value_(rtc::DSCP_NO_CHANGE), - request_manager_(thread), + request_manager_( + thread, + [this](const void* data, size_t size, StunRequest* request) { + OnSendStunPacket(data, size, request); + }), next_channel_number_(TURN_CHANNEL_NUMBER_START), state_(STATE_CONNECTING), server_priority_(server_priority), allocate_mismatch_retries_(0), - turn_customizer_(customizer) { - request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); -} + turn_customizer_(customizer), + field_trials_(field_trials) {} TurnPort::~TurnPort() { // TODO(juberti): Should this even be necessary? @@ -291,6 +310,10 @@ TurnPort::~TurnPort() { while (!entries_.empty()) { DestroyEntry(entries_.front()); } + + if (socket_) + socket_->UnsubscribeClose(this); + if (!SharedSocket()) { delete socket_; } @@ -338,7 +361,7 @@ void TurnPort::PrepareAddress() { server_address_.address.SetPort(TURN_DEFAULT_PORT); } - if (!AllowedTurnPort(server_address_.address.port())) { + if (!AllowedTurnPort(server_address_.address.port(), field_trials_)) { // This can only happen after a 300 ALTERNATE SERVER, since the port can't // be created with a disallowed port number. RTC_LOG(LS_ERROR) << "Attempt to start allocation with disallowed port# " @@ -436,7 +459,9 @@ bool TurnPort::CreateTurnClientSocket() { if (server_address_.proto == PROTO_TCP || server_address_.proto == PROTO_TLS) { socket_->SignalConnect.connect(this, &TurnPort::OnSocketConnect); - socket_->SignalClose.connect(this, &TurnPort::OnSocketClose); + socket_->SubscribeClose(this, [this](rtc::AsyncPacketSocket* s, int err) { + OnSocketClose(s, err); + }); } else { state_ = STATE_CONNECTED; } @@ -527,6 +552,9 @@ void TurnPort::OnAllocateMismatch() { << ": Allocating a new socket after " "STUN_ERROR_ALLOCATION_MISMATCH, retry: " << allocate_mismatch_retries_ + 1; + + socket_->UnsubscribeClose(this); + if (SharedSocket()) { ResetSharedSocket(); } else { @@ -557,7 +585,7 @@ Connection* TurnPort::CreateConnection(const Candidate& remote_candidate, return nullptr; } - // A TURN port will have two candiates, STUN and TURN. STUN may not + // A TURN port will have two candidates, STUN and TURN. STUN may not // present in all cases. If present stun candidate will be added first // and TURN candidate later. for (size_t index = 0; index < Candidates().size(); ++index) { @@ -573,7 +601,7 @@ Connection* TurnPort::CreateConnection(const Candidate& remote_candidate, next_channel_number_++; } ProxyConnection* conn = - new ProxyConnection(this, index, remote_candidate); + new ProxyConnection(NewWeakPtr(), index, remote_candidate); AddOrReplaceConnection(conn); return conn; } @@ -930,7 +958,8 @@ rtc::DiffServCodePoint TurnPort::StunDscpValue() const { } // static -bool TurnPort::AllowedTurnPort(int port) { +bool TurnPort::AllowedTurnPort(int port, + const webrtc::FieldTrialsView* field_trials) { // Port 53, 80 and 443 are used for existing deployments. // Ports above 1024 are assumed to be OK to use. if (port == 53 || port == 80 || port == 443 || port >= 1024) { @@ -938,7 +967,7 @@ bool TurnPort::AllowedTurnPort(int port) { } // Allow any port if relevant field trial is set. This allows disabling the // check. - if (webrtc::field_trial::IsEnabled("WebRTC-Turn-AllowSystemPorts")) { + if (field_trials && field_trials->IsEnabled("WebRTC-Turn-AllowSystemPorts")) { return true; } return false; @@ -1228,7 +1257,8 @@ bool TurnPort::CreateOrRefreshEntry(const rtc::SocketAddress& addr, RTC_DCHECK(GetConnection(addr)); } - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { + if (field_trials_ && + field_trials_->IsEnabled("WebRTC-TurnAddMultiMapping")) { if (entry->get_remote_ufrag() != remote_ufrag) { RTC_LOG(LS_INFO) << ToString() << ": remote ufrag updated." @@ -1344,20 +1374,21 @@ void TurnPort::MaybeAddTurnLoggingId(StunMessage* msg) { } TurnAllocateRequest::TurnAllocateRequest(TurnPort* port) - : StunRequest(new TurnMessage()), port_(port) {} + : StunRequest(port->request_manager(), std::make_unique()), + port_(port) {} -void TurnAllocateRequest::Prepare(StunMessage* request) { +void TurnAllocateRequest::Prepare(StunMessage* message) { // Create the request as indicated in RFC 5766, Section 6.1. - request->SetType(TURN_ALLOCATE_REQUEST); + message->SetType(TURN_ALLOCATE_REQUEST); auto transport_attr = StunAttribute::CreateUInt32(STUN_ATTR_REQUESTED_TRANSPORT); transport_attr->SetValue(IPPROTO_UDP << 24); - request->AddAttribute(std::move(transport_attr)); + message->AddAttribute(std::move(transport_attr)); if (!port_->hash().empty()) { - port_->AddRequestAuthInfo(request); + port_->AddRequestAuthInfo(message); } - port_->MaybeAddTurnLoggingId(request); - port_->TurnCustomizerMaybeModifyOutgoingStunMessage(request); + port_->MaybeAddTurnLoggingId(message); + port_->TurnCustomizerMaybeModifyOutgoingStunMessage(message); } void TurnAllocateRequest::OnSent() { @@ -1532,19 +1563,21 @@ void TurnAllocateRequest::OnTryAlternate(StunMessage* response, int code) { } TurnRefreshRequest::TurnRefreshRequest(TurnPort* port) - : StunRequest(new TurnMessage()), port_(port), lifetime_(-1) {} + : StunRequest(port->request_manager(), std::make_unique()), + port_(port), + lifetime_(-1) {} -void TurnRefreshRequest::Prepare(StunMessage* request) { +void TurnRefreshRequest::Prepare(StunMessage* message) { // Create the request as indicated in RFC 5766, Section 7.1. // No attributes need to be included. - request->SetType(TURN_REFRESH_REQUEST); + message->SetType(TURN_REFRESH_REQUEST); if (lifetime_ > -1) { - request->AddAttribute( + message->AddAttribute( std::make_unique(STUN_ATTR_LIFETIME, lifetime_)); } - port_->AddRequestAuthInfo(request); - port_->TurnCustomizerMaybeModifyOutgoingStunMessage(request); + port_->AddRequestAuthInfo(message); + port_->TurnCustomizerMaybeModifyOutgoingStunMessage(message); } void TurnRefreshRequest::OnSent() { @@ -1613,7 +1646,7 @@ TurnCreatePermissionRequest::TurnCreatePermissionRequest( TurnEntry* entry, const rtc::SocketAddress& ext_addr, const std::string& remote_ufrag) - : StunRequest(new TurnMessage()), + : StunRequest(port->request_manager(), std::make_unique()), port_(port), entry_(entry), ext_addr_(ext_addr), @@ -1622,17 +1655,18 @@ TurnCreatePermissionRequest::TurnCreatePermissionRequest( this, &TurnCreatePermissionRequest::OnEntryDestroyed); } -void TurnCreatePermissionRequest::Prepare(StunMessage* request) { +void TurnCreatePermissionRequest::Prepare(StunMessage* message) { // Create the request as indicated in RFC5766, Section 9.1. - request->SetType(TURN_CREATE_PERMISSION_REQUEST); - request->AddAttribute(std::make_unique( + message->SetType(TURN_CREATE_PERMISSION_REQUEST); + message->AddAttribute(std::make_unique( STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)); - if (webrtc::field_trial::IsEnabled("WebRTC-TurnAddMultiMapping")) { - request->AddAttribute(std::make_unique( + if (port_->field_trials_ && + port_->field_trials_->IsEnabled("WebRTC-TurnAddMultiMapping")) { + message->AddAttribute(std::make_unique( STUN_ATTR_MULTI_MAPPING, remote_ufrag_)); } - port_->AddRequestAuthInfo(request); - port_->TurnCustomizerMaybeModifyOutgoingStunMessage(request); + port_->AddRequestAuthInfo(message); + port_->TurnCustomizerMaybeModifyOutgoingStunMessage(message); } void TurnCreatePermissionRequest::OnSent() { @@ -1685,7 +1719,7 @@ TurnChannelBindRequest::TurnChannelBindRequest( TurnEntry* entry, int channel_id, const rtc::SocketAddress& ext_addr) - : StunRequest(new TurnMessage()), + : StunRequest(port->request_manager(), std::make_unique()), port_(port), entry_(entry), channel_id_(channel_id), @@ -1694,15 +1728,15 @@ TurnChannelBindRequest::TurnChannelBindRequest( &TurnChannelBindRequest::OnEntryDestroyed); } -void TurnChannelBindRequest::Prepare(StunMessage* request) { +void TurnChannelBindRequest::Prepare(StunMessage* message) { // Create the request as indicated in RFC5766, Section 11.1. - request->SetType(TURN_CHANNEL_BIND_REQUEST); - request->AddAttribute(std::make_unique( + message->SetType(TURN_CHANNEL_BIND_REQUEST); + message->AddAttribute(std::make_unique( STUN_ATTR_CHANNEL_NUMBER, channel_id_ << 16)); - request->AddAttribute(std::make_unique( + message->AddAttribute(std::make_unique( STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)); - port_->AddRequestAuthInfo(request); - port_->TurnCustomizerMaybeModifyOutgoingStunMessage(request); + port_->AddRequestAuthInfo(message); + port_->TurnCustomizerMaybeModifyOutgoingStunMessage(message); } void TurnChannelBindRequest::OnSent() { diff --git a/p2p/base/turn_port.h b/p2p/base/turn_port.h index 172dcef5ad..74d249317f 100644 --- a/p2p/base/turn_port.h +++ b/p2p/base/turn_port.h @@ -52,108 +52,56 @@ class TurnPort : public Port { // packets. }; - // Create a TURN port using the shared UDP socket, `socket`. - static std::unique_ptr Create( - rtc::Thread* thread, - rtc::PacketSocketFactory* factory, - rtc::Network* network, - rtc::AsyncPacketSocket* socket, - const std::string& username, // ice username. - const std::string& password, // ice password. - const ProtocolAddress& server_address, - const RelayCredentials& credentials, - int server_priority, - webrtc::TurnCustomizer* customizer) { + static bool Validate(const CreateRelayPortArgs& args) { // Do basic parameter validation. - if (credentials.username.size() > kMaxTurnUsernameLength) { + if (args.config->credentials.username.size() > kMaxTurnUsernameLength) { RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " - << "of length " << credentials.username.size(); - return nullptr; + << "of length " + << args.config->credentials.username.size(); + return false; } // Do not connect to low-numbered ports. The default STUN port is 3478. - if (!AllowedTurnPort(server_address.address.port())) { + if (!AllowedTurnPort(args.server_address->address.port(), + args.field_trials)) { RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " - << server_address.address.port(); + << args.server_address->address.port(); + return false; + } + return true; + } + + // Create a TURN port using the shared UDP socket, `socket`. + static std::unique_ptr Create(const CreateRelayPortArgs& args, + rtc::AsyncPacketSocket* socket) { + if (!Validate(args)) { return nullptr; } // Using `new` to access a non-public constructor. return absl::WrapUnique( - new TurnPort(thread, factory, network, socket, username, password, - server_address, credentials, server_priority, customizer)); - } - - // TODO(steveanton): Remove once downstream clients have moved to `Create`. - static std::unique_ptr CreateUnique( - rtc::Thread* thread, - rtc::PacketSocketFactory* factory, - rtc::Network* network, - rtc::AsyncPacketSocket* socket, - const std::string& username, // ice username. - const std::string& password, // ice password. - const ProtocolAddress& server_address, - const RelayCredentials& credentials, - int server_priority, - webrtc::TurnCustomizer* customizer) { - return Create(thread, factory, network, socket, username, password, - server_address, credentials, server_priority, customizer); + new TurnPort(args.network_thread, args.socket_factory, args.network, + socket, args.username, args.password, *args.server_address, + args.config->credentials, args.config->priority, + args.config->tls_alpn_protocols, + args.config->tls_elliptic_curves, args.turn_customizer, + args.config->tls_cert_verifier, args.field_trials)); } // Create a TURN port that will use a new socket, bound to `network` and // using a port in the range between `min_port` and `max_port`. - static std::unique_ptr Create( - rtc::Thread* thread, - rtc::PacketSocketFactory* factory, - rtc::Network* network, - uint16_t min_port, - uint16_t max_port, - const std::string& username, // ice username. - const std::string& password, // ice password. - const ProtocolAddress& server_address, - const RelayCredentials& credentials, - int server_priority, - const std::vector& tls_alpn_protocols, - const std::vector& tls_elliptic_curves, - webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { - // Do basic parameter validation. - if (credentials.username.size() > kMaxTurnUsernameLength) { - RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username " - << "of length " << credentials.username.size(); - return nullptr; - } - // Do not connect to low-numbered ports. The default STUN port is 3478. - if (!AllowedTurnPort(server_address.address.port())) { - RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port " - << server_address.address.port(); + static std::unique_ptr Create(const CreateRelayPortArgs& args, + int min_port, + int max_port) { + if (!Validate(args)) { return nullptr; } // Using `new` to access a non-public constructor. - return absl::WrapUnique(new TurnPort( - thread, factory, network, min_port, max_port, username, password, - server_address, credentials, server_priority, tls_alpn_protocols, - tls_elliptic_curves, customizer, tls_cert_verifier)); - } - - // TODO(steveanton): Remove once downstream clients have moved to `Create`. - static std::unique_ptr CreateUnique( - rtc::Thread* thread, - rtc::PacketSocketFactory* factory, - rtc::Network* network, - uint16_t min_port, - uint16_t max_port, - const std::string& username, // ice username. - const std::string& password, // ice password. - const ProtocolAddress& server_address, - const RelayCredentials& credentials, - int server_priority, - const std::vector& tls_alpn_protocols, - const std::vector& tls_elliptic_curves, - webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr) { - return Create(thread, factory, network, min_port, max_port, username, - password, server_address, credentials, server_priority, - tls_alpn_protocols, tls_elliptic_curves, customizer, - tls_cert_verifier); + return absl::WrapUnique( + new TurnPort(args.network_thread, args.socket_factory, args.network, + min_port, max_port, args.username, args.password, + *args.server_address, args.config->credentials, + args.config->priority, args.config->tls_alpn_protocols, + args.config->tls_elliptic_curves, args.turn_customizer, + args.config->tls_cert_verifier, args.field_trials)); } ~TurnPort() override; @@ -223,6 +171,7 @@ class TurnPort : public Port { void OnAllocateMismatch(); rtc::AsyncPacketSocket* socket() const { return socket_; } + StunRequestManager& request_manager() { return request_manager_; } // Signal with resolved server address. // Parameters are port, server address and resolved server address. @@ -240,7 +189,11 @@ class TurnPort : public Port { sigslot::signal2 SignalTurnRefreshResult; sigslot::signal3 SignalCreatePermissionResult; - void FlushRequests(int msg_type) { request_manager_.Flush(msg_type); } + + void FlushRequestsForTest(int msg_type) { + request_manager_.FlushForTest(msg_type); + } + bool HasRequests() { return !request_manager_.empty(); } void set_credentials(const RelayCredentials& credentials) { credentials_ = credentials; @@ -257,18 +210,22 @@ class TurnPort : public Port { protected: TurnPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, rtc::AsyncPacketSocket* socket, const std::string& username, const std::string& password, const ProtocolAddress& server_address, const RelayCredentials& credentials, int server_priority, - webrtc::TurnCustomizer* customizer); + const std::vector& tls_alpn_protocols, + const std::vector& tls_elliptic_curves, + webrtc::TurnCustomizer* customizer, + rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr, + const webrtc::FieldTrialsView* field_trials = nullptr); TurnPort(rtc::Thread* thread, rtc::PacketSocketFactory* factory, - rtc::Network* network, + const rtc::Network* network, uint16_t min_port, uint16_t max_port, const std::string& username, @@ -279,7 +236,8 @@ class TurnPort : public Port { const std::vector& tls_alpn_protocols, const std::vector& tls_elliptic_curves, webrtc::TurnCustomizer* customizer, - rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr); + rtc::SSLCertificateVerifier* tls_cert_verifier = nullptr, + const webrtc::FieldTrialsView* field_trials = nullptr); // NOTE: This method needs to be accessible for StunPort // return true if entry was created (i.e channel_number consumed). @@ -304,7 +262,8 @@ class TurnPort : public Port { typedef std::map SocketOptionsMap; typedef std::set AttemptedServerSet; - static bool AllowedTurnPort(int port); + static bool AllowedTurnPort(int port, + const webrtc::FieldTrialsView* field_trials); void OnMessage(rtc::Message* pmsg) override; bool CreateTurnClientSocket(); @@ -410,6 +369,8 @@ class TurnPort : public Port { // must outlive the TurnPort's lifetime. webrtc::TurnCustomizer* turn_customizer_ = nullptr; + const webrtc::FieldTrialsView* field_trials_; + // Optional TurnLoggingId. // An identifier set by application that is added to TURN_ALLOCATE_REQUEST // and can be used to match client/backend logs. diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc index 44d258363e..2244b9d78d 100644 --- a/p2p/base/turn_port_unittest.cc +++ b/p2p/base/turn_port_unittest.cc @@ -41,8 +41,8 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" -#include "test/field_trial.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::SocketAddress; @@ -277,7 +277,7 @@ class TurnPortTest : public ::testing::Test, password, server_address); } - bool CreateTurnPortWithNetwork(rtc::Network* network, + bool CreateTurnPortWithNetwork(const rtc::Network* network, const std::string& username, const std::string& password, const ProtocolAddress& server_address) { @@ -288,14 +288,24 @@ class TurnPortTest : public ::testing::Test, // Version of CreateTurnPort that takes all possible parameters; all other // helper methods call this, such that "SetIceRole" and "ConnectSignals" (and // possibly other things in the future) only happen in one place. - bool CreateTurnPortWithAllParams(rtc::Network* network, + bool CreateTurnPortWithAllParams(const rtc::Network* network, const std::string& username, const std::string& password, const ProtocolAddress& server_address) { - RelayCredentials credentials(username, password); - turn_port_ = TurnPort::Create( - &main_, &socket_factory_, network, 0, 0, kIceUfrag1, kIcePwd1, - server_address, credentials, 0, {}, {}, turn_customizer_.get()); + RelayServerConfig config; + config.credentials = RelayCredentials(username, password); + CreateRelayPortArgs args; + args.network_thread = &main_; + args.socket_factory = &socket_factory_; + args.network = network; + args.username = kIceUfrag1; + args.password = kIcePwd1; + args.server_address = &server_address; + args.config = &config; + args.turn_customizer = turn_customizer_.get(); + args.field_trials = &field_trials_; + + turn_port_ = TurnPort::Create(args, 0, 0); if (!turn_port_) { return false; } @@ -326,10 +336,19 @@ class TurnPortTest : public ::testing::Test, &TurnPortTest::OnSocketReadPacket); } - RelayCredentials credentials(username, password); - turn_port_ = TurnPort::Create( - &main_, &socket_factory_, MakeNetwork(kLocalAddr1), socket_.get(), - kIceUfrag1, kIcePwd1, server_address, credentials, 0, nullptr); + RelayServerConfig config; + config.credentials = RelayCredentials(username, password); + CreateRelayPortArgs args; + args.network_thread = &main_; + args.socket_factory = &socket_factory_; + args.network = MakeNetwork(kLocalAddr1); + args.username = kIceUfrag1; + args.password = kIcePwd1; + args.server_address = &server_address; + args.config = &config; + args.turn_customizer = turn_customizer_.get(); + args.field_trials = &field_trials_; + turn_port_ = TurnPort::Create(args, socket_.get()); // This TURN port will be the controlling. turn_port_->SetIceRole(ICEROLE_CONTROLLING); ConnectSignals(); @@ -356,9 +375,9 @@ class TurnPortTest : public ::testing::Test, void CreateUdpPort() { CreateUdpPort(kLocalAddr2); } void CreateUdpPort(const SocketAddress& address) { - udp_port_ = - UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address), 0, 0, - kIceUfrag2, kIcePwd2, false, absl::nullopt); + udp_port_ = UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address), + 0, 0, kIceUfrag2, kIcePwd2, false, + absl::nullopt, &field_trials_); // UDP port will be controlled. udp_port_->SetIceRole(ICEROLE_CONTROLLED); udp_port_->SignalPortComplete.connect(this, @@ -760,6 +779,7 @@ class TurnPortTest : public ::testing::Test, } protected: + webrtc::test::ScopedKeyValueConfig field_trials_; rtc::ScopedFakeClock fake_clock_; // When a "create port" helper method is called with an IP, we create a // Network with that IP and add it to this list. Using a list instead of a @@ -1221,10 +1241,10 @@ TEST_F(TurnPortTest, TestRefreshRequestGetsErrorResponse) { // This sends out the first RefreshRequest with correct credentials. // When this succeeds, it will schedule a new RefreshRequest with the bad // credential. - turn_port_->FlushRequests(TURN_REFRESH_REQUEST); + turn_port_->FlushRequestsForTest(TURN_REFRESH_REQUEST); EXPECT_TRUE_SIMULATED_WAIT(turn_refresh_success_, kSimulatedRtt, fake_clock_); // Flush it again, it will receive a bad response. - turn_port_->FlushRequests(TURN_REFRESH_REQUEST); + turn_port_->FlushRequestsForTest(TURN_REFRESH_REQUEST); EXPECT_TRUE_SIMULATED_WAIT(!turn_refresh_success_, kSimulatedRtt, fake_clock_); EXPECT_FALSE(turn_port_->connected()); @@ -1285,7 +1305,7 @@ TEST_F(TurnPortTest, TestSocketCloseWillDestroyConnection) { Port::ORIGIN_MESSAGE); EXPECT_NE(nullptr, conn); EXPECT_TRUE(!turn_port_->connections().empty()); - turn_port_->socket()->SignalClose(turn_port_->socket(), 1); + turn_port_->socket()->NotifyClosedForTest(1); EXPECT_TRUE_SIMULATED_WAIT(turn_port_->connections().empty(), kConnectionDestructionDelay, fake_clock_); } @@ -1438,11 +1458,11 @@ TEST_F(TurnPortTest, TestRefreshCreatePermissionRequest) { // another request with bad_ufrag and bad_pwd. RelayCredentials bad_credentials("bad_user", "bad_pwd"); turn_port_->set_credentials(bad_credentials); - turn_port_->FlushRequests(kAllRequests); + turn_port_->FlushRequestsForTest(kAllRequests); EXPECT_TRUE_SIMULATED_WAIT(turn_create_permission_success_, kSimulatedRtt, fake_clock_); // Flush the requests again; the create-permission-request will fail. - turn_port_->FlushRequests(kAllRequests); + turn_port_->FlushRequestsForTest(kAllRequests); EXPECT_TRUE_SIMULATED_WAIT(!turn_create_permission_success_, kSimulatedRtt, fake_clock_); EXPECT_TRUE(CheckConnectionFailedAndPruned(conn)); @@ -1852,8 +1872,8 @@ TEST_F(TurnPortTest, TestTurnDangerousAlternateServer) { } TEST_F(TurnPortTest, TestTurnDangerousServerAllowedWithFieldTrial) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-Turn-AllowSystemPorts/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials_, "WebRTC-Turn-AllowSystemPorts/Enabled/"); CreateTurnPort(kTurnUsername, kTurnPassword, kTurnDangerousProtoAddr); ASSERT_TRUE(turn_port_); } diff --git a/p2p/base/turn_server.cc b/p2p/base/turn_server.cc index 863319e916..85baf75819 100644 --- a/p2p/base/turn_server.cc +++ b/p2p/base/turn_server.cc @@ -16,6 +16,7 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" +#include "api/array_view.h" #include "api/packet_socket_factory.h" #include "api/transport/stun.h" #include "p2p/base/async_stun_tcp_socket.h" @@ -194,7 +195,10 @@ void TurnServer::AcceptConnection(rtc::Socket* server_socket) { cricket::AsyncStunTCPSocket* tcp_socket = new cricket::AsyncStunTCPSocket(accepted_socket); - tcp_socket->SignalClose.connect(this, &TurnServer::OnInternalSocketClose); + tcp_socket->SubscribeClose(this, + [this](rtc::AsyncPacketSocket* s, int err) { + OnInternalSocketClose(s, err); + }); // Finally add the socket so it can start communicating with the client. AddInternalSocket(tcp_socket, info.proto); } @@ -421,7 +425,7 @@ void TurnServer::HandleAllocateRequest(TurnServerConnection* conn, std::string TurnServer::GenerateNonce(int64_t now) const { // Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now)) std::string input(reinterpret_cast(&now), sizeof(now)); - std::string nonce = rtc::hex_encode(input.c_str(), input.size()); + std::string nonce = rtc::hex_encode(input); nonce += rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_, input); RTC_DCHECK(nonce.size() == kNonceSize); @@ -437,8 +441,8 @@ bool TurnServer::ValidateNonce(const std::string& nonce) const { // Decode the timestamp. int64_t then; char* p = reinterpret_cast(&then); - size_t len = - rtc::hex_decode(p, sizeof(then), nonce.substr(0, sizeof(then) * 2)); + size_t len = rtc::hex_decode(rtc::ArrayView(p, sizeof(then)), + nonce.substr(0, sizeof(then) * 2)); if (len != sizeof(then)) { return false; } @@ -563,6 +567,7 @@ void TurnServer::DestroyInternalSocket(rtc::AsyncPacketSocket* socket) { InternalSocketMap::iterator iter = server_sockets_.find(socket); if (iter != server_sockets_.end()) { rtc::AsyncPacketSocket* socket = iter->first; + socket->UnsubscribeClose(this); socket->SignalReadPacket.disconnect(this); server_sockets_.erase(iter); std::unique_ptr socket_to_delete = diff --git a/p2p/client/basic_port_allocator.cc b/p2p/client/basic_port_allocator.cc index 06eabb83dd..f4306f1798 100644 --- a/p2p/client/basic_port_allocator.cc +++ b/p2p/client/basic_port_allocator.cc @@ -20,6 +20,7 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" +#include "api/transport/field_trial_based_config.h" #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/port.h" #include "p2p/base/stun_port.h" @@ -29,9 +30,9 @@ #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" using rtc::CreateRandomId; @@ -88,16 +89,17 @@ int ComparePort(const cricket::Port* a, const cricket::Port* b) { } struct NetworkFilter { - using Predicate = std::function; + using Predicate = std::function; NetworkFilter(Predicate pred, const std::string& description) - : predRemain([pred](rtc::Network* network) { return !pred(network); }), + : predRemain( + [pred](const rtc::Network* network) { return !pred(network); }), description(description) {} Predicate predRemain; const std::string description; }; -using NetworkList = rtc::NetworkManager::NetworkList; -void FilterNetworks(NetworkList* networks, NetworkFilter filter) { +void FilterNetworks(std::vector* networks, + NetworkFilter filter) { auto start_to_remove = std::partition(networks->begin(), networks->end(), filter.predRemain); if (start_to_remove == networks->end()) { @@ -139,6 +141,14 @@ bool IsAllowedByCandidateFilter(const Candidate& c, uint32_t filter) { return false; } +std::string NetworksToString(const std::vector& networks) { + rtc::StringBuilder ost; + for (auto n : networks) { + ost << n->name() << " "; + } + return ost.Release(); +} + } // namespace const uint32_t DISABLE_ALL_PHASES = @@ -152,7 +162,7 @@ BasicPortAllocator::BasicPortAllocator( webrtc::TurnCustomizer* customizer, RelayPortFactoryInterface* relay_port_factory) : network_manager_(network_manager), socket_factory_(socket_factory) { - InitRelayPortFactory(relay_port_factory); + Init(relay_port_factory, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); RTC_DCHECK(socket_factory_ != nullptr); @@ -162,7 +172,7 @@ BasicPortAllocator::BasicPortAllocator( BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager) : network_manager_(network_manager), socket_factory_(nullptr) { - InitRelayPortFactory(nullptr); + Init(nullptr, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); } @@ -177,7 +187,7 @@ BasicPortAllocator::BasicPortAllocator(rtc::NetworkManager* network_manager, rtc::PacketSocketFactory* socket_factory, const ServerAddresses& stun_servers) : network_manager_(network_manager), socket_factory_(socket_factory) { - InitRelayPortFactory(nullptr); + Init(nullptr, nullptr); RTC_DCHECK(relay_port_factory_ != nullptr); RTC_DCHECK(network_manager_ != nullptr); SetConfiguration(stun_servers, std::vector(), 0, @@ -251,14 +261,21 @@ void BasicPortAllocator::AddTurnServer(const RelayServerConfig& turn_server) { turn_port_prune_policy(), turn_customizer()); } -void BasicPortAllocator::InitRelayPortFactory( - RelayPortFactoryInterface* relay_port_factory) { +void BasicPortAllocator::Init(RelayPortFactoryInterface* relay_port_factory, + const webrtc::FieldTrialsView* field_trials) { if (relay_port_factory != nullptr) { relay_port_factory_ = relay_port_factory; } else { default_relay_port_factory_.reset(new TurnPortFactory()); relay_port_factory_ = default_relay_port_factory_.get(); } + + if (field_trials != nullptr) { + field_trials_ = field_trials; + } else { + owned_field_trials_ = std::make_unique(); + field_trials_ = owned_field_trials_.get(); + } } // BasicPortAllocatorSession @@ -424,11 +441,11 @@ bool BasicPortAllocatorSession::IsStopped() const { return state_ == SessionState::STOPPED; } -std::vector BasicPortAllocatorSession::GetFailedNetworks() { +std::vector +BasicPortAllocatorSession::GetFailedNetworks() { RTC_DCHECK_RUN_ON(network_thread_); - std::vector networks = GetNetworks(); - + std::vector networks = GetNetworks(); // A network interface may have both IPv4 and IPv6 networks. Only if // neither of the networks has any connections, the network interface // is considered failed and need to be regathered on. @@ -442,7 +459,7 @@ std::vector BasicPortAllocatorSession::GetFailedNetworks() { networks.erase( std::remove_if(networks.begin(), networks.end(), - [networks_with_connection](rtc::Network* network) { + [networks_with_connection](const rtc::Network* network) { // If a network does not have any connection, it is // considered failed. return networks_with_connection.find(network->name()) != @@ -456,7 +473,7 @@ void BasicPortAllocatorSession::RegatherOnFailedNetworks() { RTC_DCHECK_RUN_ON(network_thread_); // Find the list of networks that have no connection. - std::vector failed_networks = GetFailedNetworks(); + std::vector failed_networks = GetFailedNetworks(); if (failed_networks.empty()) { return; } @@ -479,7 +496,7 @@ void BasicPortAllocatorSession::RegatherOnFailedNetworks() { } void BasicPortAllocatorSession::Regather( - const std::vector& networks, + const std::vector& networks, bool disable_equivalent_phases, IceRegatheringReason reason) { RTC_DCHECK_RUN_ON(network_thread_); @@ -602,8 +619,9 @@ void BasicPortAllocatorSession::UpdateIceParametersInternal() { void BasicPortAllocatorSession::GetPortConfigurations() { RTC_DCHECK_RUN_ON(network_thread_); - auto config = std::make_unique(allocator_->stun_servers(), - username(), password()); + auto config = std::make_unique( + allocator_->stun_servers(), username(), password(), + allocator()->field_trials()); for (const RelayServerConfig& turn_server : allocator_->turn_servers()) { config->AddRelay(turn_server); @@ -687,9 +705,9 @@ void BasicPortAllocatorSession::OnAllocate(int allocation_epoch) { allocation_started_ = true; } -std::vector BasicPortAllocatorSession::GetNetworks() { +std::vector BasicPortAllocatorSession::GetNetworks() { RTC_DCHECK_RUN_ON(network_thread_); - std::vector networks; + std::vector networks; rtc::NetworkManager* network_manager = allocator_->network_manager(); RTC_DCHECK(network_manager != nullptr); // If the network permission state is BLOCKED, we just act as if the flag has @@ -703,35 +721,41 @@ std::vector BasicPortAllocatorSession::GetNetworks() { // traffic by OS is also used here to avoid any local or public IP leakage // during stun process. if (flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) { - network_manager->GetAnyAddressNetworks(&networks); + networks = network_manager->GetAnyAddressNetworks(); } else { - network_manager->GetNetworks(&networks); + networks = network_manager->GetNetworks(); // If network enumeration fails, use the ANY address as a fallback, so we // can at least try gathering candidates using the default route chosen by // the OS. Or, if the PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS flag is // set, we'll use ANY address candidates either way. - if (networks.empty() || flags() & PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS) { - network_manager->GetAnyAddressNetworks(&networks); + if (networks.empty() || + (flags() & PORTALLOCATOR_ENABLE_ANY_ADDRESS_PORTS)) { + std::vector any_address_networks = + network_manager->GetAnyAddressNetworks(); + networks.insert(networks.end(), any_address_networks.begin(), + any_address_networks.end()); } } // Filter out link-local networks if needed. if (flags() & PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS) { NetworkFilter link_local_filter( - [](rtc::Network* network) { return IPIsLinkLocal(network->prefix()); }, + [](const rtc::Network* network) { + return IPIsLinkLocal(network->prefix()); + }, "link-local"); FilterNetworks(&networks, link_local_filter); } // Do some more filtering, depending on the network ignore mask and "disable // costly networks" flag. NetworkFilter ignored_filter( - [this](rtc::Network* network) { + [this](const rtc::Network* network) { return allocator_->GetNetworkIgnoreMask() & network->type(); }, "ignored"); FilterNetworks(&networks, ignored_filter); if (flags() & PORTALLOCATOR_DISABLE_COSTLY_NETWORKS) { uint16_t lowest_cost = rtc::kNetworkCostMax; - for (rtc::Network* network : networks) { + for (const rtc::Network* network : networks) { // Don't determine the lowest cost from a link-local network. // On iOS, a device connected to the computer will get a link-local // network for communicating with the computer, however this network can't @@ -739,11 +763,13 @@ std::vector BasicPortAllocatorSession::GetNetworks() { if (rtc::IPIsLinkLocal(network->GetBestIP())) { continue; } - lowest_cost = std::min(lowest_cost, network->GetCost()); + lowest_cost = std::min( + lowest_cost, network->GetCost(*allocator()->field_trials())); } NetworkFilter costly_filter( - [lowest_cost](rtc::Network* network) { - return network->GetCost() > lowest_cost + rtc::kNetworkCostLow; + [lowest_cost, this](const rtc::Network* network) { + return network->GetCost(*allocator()->field_trials()) > + lowest_cost + rtc::kNetworkCostLow; }, "costly"); FilterNetworks(&networks, costly_filter); @@ -777,13 +803,13 @@ std::vector BasicPortAllocatorSession::GetNetworks() { void BasicPortAllocatorSession::DoAllocate(bool disable_equivalent) { RTC_DCHECK_RUN_ON(network_thread_); bool done_signal_needed = false; - std::vector networks = GetNetworks(); + std::vector networks = GetNetworks(); if (networks.empty()) { RTC_LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated"; done_signal_needed = true; } else { - RTC_LOG(LS_INFO) << "Allocate ports on " << networks.size() << " networks"; + RTC_LOG(LS_INFO) << "Allocate ports on " << NetworksToString(networks); PortConfiguration* config = configs_.empty() ? nullptr : configs_.back().get(); for (uint32_t i = 0; i < networks.size(); ++i) { @@ -844,8 +870,8 @@ void BasicPortAllocatorSession::DoAllocate(bool disable_equivalent) { void BasicPortAllocatorSession::OnNetworksChanged() { RTC_DCHECK_RUN_ON(network_thread_); - std::vector networks = GetNetworks(); - std::vector failed_networks; + std::vector networks = GetNetworks(); + std::vector failed_networks; for (AllocationSequence* sequence : sequences_) { // Mark the sequence as "network failed" if its network is not in // `networks`. @@ -878,7 +904,7 @@ void BasicPortAllocatorSession::OnNetworksChanged() { } void BasicPortAllocatorSession::DisableEquivalentPhases( - rtc::Network* network, + const rtc::Network* network, PortConfiguration* config, uint32_t* flags) { RTC_DCHECK_RUN_ON(network_thread_); @@ -1191,7 +1217,7 @@ BasicPortAllocatorSession::PortData* BasicPortAllocatorSession::FindPort( std::vector BasicPortAllocatorSession::GetUnprunedPorts( - const std::vector& networks) { + const std::vector& networks) { RTC_DCHECK_RUN_ON(network_thread_); std::vector unpruned_ports; for (PortData& port : ports_) { @@ -1238,7 +1264,7 @@ void BasicPortAllocator::SetVpnList( AllocationSequence::AllocationSequence( BasicPortAllocatorSession* session, - rtc::Network* network, + const rtc::Network* network, PortConfiguration* config, uint32_t flags, std::function port_allocation_complete_callback) @@ -1280,7 +1306,7 @@ void AllocationSequence::OnNetworkFailed() { Stop(); } -void AllocationSequence::DisableEquivalentPhases(rtc::Network* network, +void AllocationSequence::DisableEquivalentPhases(const rtc::Network* network, PortConfiguration* config, uint32_t* flags) { if (network_failed_) { @@ -1432,14 +1458,16 @@ void AllocationSequence::CreateUDPPorts() { session_->network_thread(), session_->socket_factory(), network_, udp_socket_.get(), session_->username(), session_->password(), emit_local_candidate_for_anyaddress, - session_->allocator()->stun_candidate_keepalive_interval()); + session_->allocator()->stun_candidate_keepalive_interval(), + session_->allocator()->field_trials()); } else { port = UDPPort::Create( session_->network_thread(), session_->socket_factory(), network_, session_->allocator()->min_port(), session_->allocator()->max_port(), session_->username(), session_->password(), emit_local_candidate_for_anyaddress, - session_->allocator()->stun_candidate_keepalive_interval()); + session_->allocator()->stun_candidate_keepalive_interval(), + session_->allocator()->field_trials()); } if (port) { @@ -1475,7 +1503,8 @@ void AllocationSequence::CreateTCPPorts() { session_->network_thread(), session_->socket_factory(), network_, session_->allocator()->min_port(), session_->allocator()->max_port(), session_->username(), session_->password(), - session_->allocator()->allow_tcp_listen()); + session_->allocator()->allow_tcp_listen(), + session_->allocator()->field_trials()); if (port) { session_->AddAllocatedPort(port.release(), this); // Since TCPPort is not created using shared socket, `port` will not be @@ -1503,7 +1532,8 @@ void AllocationSequence::CreateStunPorts() { session_->network_thread(), session_->socket_factory(), network_, session_->allocator()->min_port(), session_->allocator()->max_port(), session_->username(), session_->password(), config_->StunServers(), - session_->allocator()->stun_candidate_keepalive_interval()); + session_->allocator()->stun_candidate_keepalive_interval(), + session_->allocator()->field_trials()); if (port) { session_->AddAllocatedPort(port.release(), this); // Since StunPort is not created using shared socket, `port` will not be @@ -1565,6 +1595,7 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { args.server_address = &(*relay_port); args.config = &config; args.turn_customizer = session_->allocator()->turn_customizer(); + args.field_trials = session_->allocator()->field_trials(); std::unique_ptr port; // Shared socket mode must be enabled only for UDP based ports. Hence @@ -1658,24 +1689,19 @@ void AllocationSequence::OnPortDestroyed(PortInterface* port) { } } -// PortConfiguration -PortConfiguration::PortConfiguration(const rtc::SocketAddress& stun_address, - const std::string& username, - const std::string& password) - : stun_address(stun_address), username(username), password(password) { - if (!stun_address.IsNil()) - stun_servers.insert(stun_address); -} - -PortConfiguration::PortConfiguration(const ServerAddresses& stun_servers, - const std::string& username, - const std::string& password) +PortConfiguration::PortConfiguration( + const ServerAddresses& stun_servers, + const std::string& username, + const std::string& password, + const webrtc::FieldTrialsView* field_trials) : stun_servers(stun_servers), username(username), password(password) { if (!stun_servers.empty()) stun_address = *(stun_servers.begin()); // Note that this won't change once the config is initialized. - use_turn_server_as_stun_server_disabled = - webrtc::field_trial::IsDisabled("WebRTC-UseTurnServerAsStunServer"); + if (field_trials) { + use_turn_server_as_stun_server_disabled = + field_trials->IsDisabled("WebRTC-UseTurnServerAsStunServer"); + } } ServerAddresses PortConfiguration::StunServers() { diff --git a/p2p/client/basic_port_allocator.h b/p2p/client/basic_port_allocator.h index b1dc7b12a2..01d07e76b3 100644 --- a/p2p/client/basic_port_allocator.h +++ b/p2p/client/basic_port_allocator.h @@ -15,6 +15,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/turn_customizer.h" #include "p2p/base/port_allocator.h" #include "p2p/client/relay_port_factory_interface.h" @@ -80,15 +81,21 @@ class RTC_EXPORT BasicPortAllocator : public PortAllocator { void SetVpnList(const std::vector& vpn_list) override; + const webrtc::FieldTrialsView* field_trials() const { return field_trials_; } + private: void OnIceRegathering(PortAllocatorSession* session, IceRegatheringReason reason); - // This function makes sure that relay_port_factory_ is set properly. - void InitRelayPortFactory(RelayPortFactoryInterface* relay_port_factory); + // This function makes sure that relay_port_factory_ and field_trials_ is set + // properly. + void Init(RelayPortFactoryInterface* relay_port_factory, + const webrtc::FieldTrialsView* field_trials); bool MdnsObfuscationEnabled() const override; + const webrtc::FieldTrialsView* field_trials_; + std::unique_ptr owned_field_trials_; rtc::NetworkManager* network_manager_; rtc::PacketSocketFactory* socket_factory_; int network_ignore_mask_ = rtc::kDefaultNetworkIgnoreMask; @@ -227,7 +234,7 @@ class RTC_EXPORT BasicPortAllocatorSession : public PortAllocatorSession { void DoAllocate(bool disable_equivalent_phases); void OnNetworksChanged(); void OnAllocationSequenceObjectsCreated(); - void DisableEquivalentPhases(rtc::Network* network, + void DisableEquivalentPhases(const rtc::Network* network, PortConfiguration* config, uint32_t* flags); void AddAllocatedPort(Port* port, AllocationSequence* seq); @@ -240,9 +247,9 @@ class RTC_EXPORT BasicPortAllocatorSession : public PortAllocatorSession { void MaybeSignalCandidatesAllocationDone(); void OnPortAllocationComplete(); PortData* FindPort(Port* port); - std::vector GetNetworks(); - std::vector GetFailedNetworks(); - void Regather(const std::vector& networks, + std::vector GetNetworks(); + std::vector GetFailedNetworks(); + void Regather(const std::vector& networks, bool disable_equivalent_phases, IceRegatheringReason reason); @@ -250,7 +257,7 @@ class RTC_EXPORT BasicPortAllocatorSession : public PortAllocatorSession { bool CandidatePairable(const Candidate& c, const Port* port) const; std::vector GetUnprunedPorts( - const std::vector& networks); + const std::vector& networks); // Prunes ports and signal the remote side to remove the candidates that // were previously signaled from these ports. void PrunePortsAndRemoveCandidates( @@ -298,14 +305,10 @@ struct RTC_EXPORT PortConfiguration { typedef std::vector RelayList; RelayList relays; - // TODO(jiayl): remove this ctor when Chrome is updated. - PortConfiguration(const rtc::SocketAddress& stun_address, - const std::string& username, - const std::string& password); - PortConfiguration(const ServerAddresses& stun_servers, const std::string& username, - const std::string& password); + const std::string& password, + const webrtc::FieldTrialsView* field_trials = nullptr); // Returns addresses of both the explicitly configured STUN servers, // and TURN servers that should be used as STUN servers. @@ -347,7 +350,7 @@ class AllocationSequence : public sigslot::has_slots<> { // event to trigger signal. This can also be achieved by starting a timer in // BPAS, but this is less deterministic. AllocationSequence(BasicPortAllocatorSession* session, - rtc::Network* network, + const rtc::Network* network, PortConfiguration* config, uint32_t flags, std::function port_allocation_complete_callback); @@ -356,14 +359,14 @@ class AllocationSequence : public sigslot::has_slots<> { void OnNetworkFailed(); State state() const { return state_; } - rtc::Network* network() const { return network_; } + const rtc::Network* network() const { return network_; } bool network_failed() const { return network_failed_; } void set_network_failed() { network_failed_ = true; } // Disables the phases for a new sequence that this one already covers for an // equivalent network setup. - void DisableEquivalentPhases(rtc::Network* network, + void DisableEquivalentPhases(const rtc::Network* network, PortConfiguration* config, uint32_t* flags); @@ -396,7 +399,7 @@ class AllocationSequence : public sigslot::has_slots<> { BasicPortAllocatorSession* session_; bool network_failed_ = false; - rtc::Network* network_; + const rtc::Network* network_; // Compared with the new best IP in DisableEquivalentPhases. rtc::IPAddress previous_best_ip_; PortConfiguration* config_; diff --git a/p2p/client/basic_port_allocator_unittest.cc b/p2p/client/basic_port_allocator_unittest.cc index 6db82d2e30..aa04b78855 100644 --- a/p2p/client/basic_port_allocator_unittest.cc +++ b/p2p/client/basic_port_allocator_unittest.cc @@ -42,9 +42,9 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::IPAddress; using rtc::SocketAddress; @@ -2444,12 +2444,12 @@ TEST_F(BasicPortAllocatorTest, TestUseTurnServerAsStunSever) { } TEST_F(BasicPortAllocatorTest, TestDoNotUseTurnServerAsStunSever) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-UseTurnServerAsStunServer/Disabled/"); ServerAddresses stun_servers; stun_servers.insert(kStunAddr); PortConfiguration port_config(stun_servers, "" /* user_name */, - "" /* password */); + "" /* password */, &field_trials); RelayServerConfig turn_servers = CreateTurnServers(kTurnUdpIntAddr, kTurnTcpIntAddr); port_config.AddRelay(turn_servers); diff --git a/p2p/client/relay_port_factory_interface.h b/p2p/client/relay_port_factory_interface.h index d3884126a6..4eec5dbf28 100644 --- a/p2p/client/relay_port_factory_interface.h +++ b/p2p/client/relay_port_factory_interface.h @@ -26,6 +26,7 @@ class Thread; namespace webrtc { class TurnCustomizer; +class FieldTrialsView; } // namespace webrtc namespace cricket { @@ -35,19 +36,17 @@ struct RelayServerConfig; // A struct containing arguments to RelayPortFactory::Create() struct CreateRelayPortArgs { - CreateRelayPortArgs(); rtc::Thread* network_thread; rtc::PacketSocketFactory* socket_factory; - rtc::Network* network; + const rtc::Network* network; const ProtocolAddress* server_address; const RelayServerConfig* config; std::string username; std::string password; - webrtc::TurnCustomizer* turn_customizer; + webrtc::TurnCustomizer* turn_customizer = nullptr; + const webrtc::FieldTrialsView* field_trials = nullptr; }; -inline CreateRelayPortArgs::CreateRelayPortArgs() {} - // A factory for creating RelayPort's. class RelayPortFactoryInterface { public: diff --git a/p2p/client/turn_port_factory.cc b/p2p/client/turn_port_factory.cc index feaada3a1c..555387dbbf 100644 --- a/p2p/client/turn_port_factory.cc +++ b/p2p/client/turn_port_factory.cc @@ -23,10 +23,7 @@ TurnPortFactory::~TurnPortFactory() {} std::unique_ptr TurnPortFactory::Create( const CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) { - auto port = TurnPort::CreateUnique( - args.network_thread, args.socket_factory, args.network, udp_socket, - args.username, args.password, *args.server_address, - args.config->credentials, args.config->priority, args.turn_customizer); + auto port = TurnPort::Create(args, udp_socket); if (!port) return nullptr; port->SetTlsCertPolicy(args.config->tls_cert_policy); @@ -37,12 +34,7 @@ std::unique_ptr TurnPortFactory::Create( std::unique_ptr TurnPortFactory::Create(const CreateRelayPortArgs& args, int min_port, int max_port) { - auto port = TurnPort::CreateUnique( - args.network_thread, args.socket_factory, args.network, min_port, - max_port, args.username, args.password, *args.server_address, - args.config->credentials, args.config->priority, - args.config->tls_alpn_protocols, args.config->tls_elliptic_curves, - args.turn_customizer, args.config->tls_cert_verifier); + auto port = TurnPort::Create(args, min_port, max_port); if (!port) return nullptr; port->SetTlsCertPolicy(args.config->tls_cert_policy); diff --git a/p2p/stunprober/stun_prober.cc b/p2p/stunprober/stun_prober.cc index efe0fbdea8..be6da3982b 100644 --- a/p2p/stunprober/stun_prober.cc +++ b/p2p/stunprober/stun_prober.cc @@ -255,11 +255,11 @@ void StunProber::ObserverAdapter::OnFinished(StunProber* stunprober, StunProber::StunProber(rtc::PacketSocketFactory* socket_factory, rtc::Thread* thread, - const rtc::NetworkManager::NetworkList& networks) + std::vector networks) : interval_ms_(0), socket_factory_(socket_factory), thread_(thread), - networks_(networks) {} + networks_(std::move(networks)) {} StunProber::~StunProber() { RTC_DCHECK(thread_checker_.IsCurrent()); diff --git a/p2p/stunprober/stun_prober.h b/p2p/stunprober/stun_prober.h index b1acd7704d..1fe7debc04 100644 --- a/p2p/stunprober/stun_prober.h +++ b/p2p/stunprober/stun_prober.h @@ -97,7 +97,7 @@ class RTC_EXPORT StunProber : public sigslot::has_slots<> { StunProber(rtc::PacketSocketFactory* socket_factory, rtc::Thread* thread, - const rtc::NetworkManager::NetworkList& networks); + std::vector networks); ~StunProber() override; StunProber(const StunProber&) = delete; @@ -240,7 +240,7 @@ class RTC_EXPORT StunProber : public sigslot::has_slots<> { // AsyncCallback. ObserverAdapter observer_adapter_; - rtc::NetworkManager::NetworkList networks_; + const std::vector networks_; webrtc::ScopedTaskSafety task_safety_; }; diff --git a/p2p/stunprober/stun_prober_unittest.cc b/p2p/stunprober/stun_prober_unittest.cc index 6ba2c423d2..b57f93b634 100644 --- a/p2p/stunprober/stun_prober_unittest.cc +++ b/p2p/stunprober/stun_prober_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include #include "p2p/base/basic_packet_socket_factory.h" #include "p2p/base/test_stun_server.h" @@ -40,7 +41,7 @@ const rtc::SocketAddress kStunMappedAddr("77.77.77.77", 0); class StunProberTest : public ::testing::Test { public: StunProberTest() - : ss_(new rtc::VirtualSocketServer()), + : ss_(std::make_unique()), main_(ss_.get()), result_(StunProber::SUCCESS), stun_server_1_(cricket::TestStunServer::Create(ss_.get(), kStunAddr1)), @@ -54,16 +55,17 @@ class StunProberTest : public ::testing::Test { void StartProbing(rtc::PacketSocketFactory* socket_factory, const std::vector& addrs, - const rtc::NetworkManager::NetworkList& networks, + std::vector networks, bool shared_socket, uint16_t interval, uint16_t pings_per_ip) { - prober.reset( - new StunProber(socket_factory, rtc::Thread::Current(), networks)); - prober->Start(addrs, shared_socket, interval, pings_per_ip, - 100 /* timeout_ms */, [this](StunProber* prober, int result) { - this->StopCallback(prober, result); - }); + prober_ = std::make_unique( + socket_factory, rtc::Thread::Current(), std::move(networks)); + prober_->Start(addrs, shared_socket, interval, pings_per_ip, + 100 /* timeout_ms */, + [this](StunProber* prober, int result) { + StopCallback(prober, result); + }); } void RunProber(bool shared_mode) { @@ -77,7 +79,7 @@ class StunProberTest : public ::testing::Test { rtc::Network ipv4_network1("test_eth0", "Test Network Adapter 1", rtc::IPAddress(0x12345600U), 24); ipv4_network1.AddIP(rtc::IPAddress(0x12345678)); - rtc::NetworkManager::NetworkList networks; + std::vector networks; networks.push_back(&ipv4_network1); auto socket_factory = @@ -93,13 +95,13 @@ class StunProberTest : public ::testing::Test { // kFailedStunAddr. const uint32_t total_pings_reported = total_pings_tried - pings_per_ip; - StartProbing(socket_factory.get(), addrs, networks, shared_mode, 3, - pings_per_ip); + StartProbing(socket_factory.get(), addrs, std::move(networks), shared_mode, + 3, pings_per_ip); WAIT(stopped_, 1000); StunProber::Stats stats; - EXPECT_TRUE(prober->GetStats(&stats)); + EXPECT_TRUE(prober_->GetStats(&stats)); EXPECT_EQ(stats.success_percent, 100); EXPECT_TRUE(stats.nat_type > stunprober::NATTYPE_NONE); EXPECT_EQ(stats.srflx_addrs, srflx_addresses); @@ -117,7 +119,7 @@ class StunProberTest : public ::testing::Test { std::unique_ptr ss_; rtc::AutoSocketServerThread main_; - std::unique_ptr prober; + std::unique_ptr prober_; int result_ = 0; bool stopped_ = false; std::unique_ptr stun_server_1_; diff --git a/pc/BUILD.gn b/pc/BUILD.gn index e7b7cc0102..066723547a 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -13,7 +13,6 @@ # Some functions are cleared for wider webrtc usage; these have default # visibility (set to "//*", not the gn default of "*"). # These are: -# - rtc_pc_base # - rtc_pc # - session_description # - simulcast_description @@ -40,13 +39,6 @@ group("pc") { deps = [ ":rtc_pc" ] } -config("rtc_pc_config") { - defines = [] - if (rtc_enable_sctp) { - defines += [ "WEBRTC_HAVE_SCTP" ] - } -} - rtc_library("proxy") { visibility = [ ":*" ] sources = [ @@ -56,149 +48,655 @@ rtc_library("proxy") { deps = [ "../api:scoped_refptr", "../api/task_queue", - "../rtc_base:rtc_base_approved", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:refcount", + "../rtc_base:rtc_event", + "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base/system:rtc_export", ] } -rtc_library("rtc_pc_base") { - # TODO(bugs.webrtc.org/13661): Reduce visibility if possible - visibility = [ "*" ] # Used by Chromium and others - defines = [] +rtc_source_set("channel") { + visibility = [ + ":*", + "../test/peer_scenario", + ] sources = [ "channel.cc", "channel.h", - "channel_interface.h", + ] + deps = [ + ":channel_interface", + ":rtp_media_utils", + ":rtp_transport_internal", + ":session_description", + "../api:libjingle_peerconnection_api", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/crypto:options", + "../api/units:timestamp", + "../call:rtp_interfaces", + "../call:rtp_receiver", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:socket", + "../rtc_base:stringutils", + "../rtc_base:threading", + "../rtc_base/containers:flat_set", + "../rtc_base/network:sent_packet", + "../rtc_base/task_utils:pending_task_safety_flag", + "../rtc_base/task_utils:to_queued_task", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("channel_interface") { + visibility = [ ":*" ] + sources = [ "channel_interface.h" ] + deps = [ + ":rtp_transport_internal", + "../api:libjingle_peerconnection_api", + "../api:rtp_parameters", + "../media:rtc_media_base", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_source_set("channel_factory_interface") { + visibility = [ ":*" ] + sources = [ "channel_factory_interface.h" ] + deps = [ + "../api:audio_options_api", + "../api/crypto:options", + "../api/video:video_bitrate_allocator_factory", + "../call:call_interfaces", + "../media:rtc_media_base", + "../media:rtc_media_config", + ] +} + +rtc_source_set("channel_manager") { + visibility = [ ":*" ] + sources = [ "channel_manager.cc", "channel_manager.h", + ] + deps = [ + ":channel", + ":channel_factory_interface", + ":channel_interface", + ":session_description", + "../api:audio_options_api", + "../api:rtp_parameters", + "../api:sequence_checker", + "../api/crypto:options", + "../api/video:video_bitrate_allocator_factory", + "../call:call_interfaces", + "../media:rtc_media_base", + "../media:rtc_media_config", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:macromagic", + "../rtc_base:threading", + "../rtc_base/system:file_wrapper", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] +} + +rtc_source_set("dtls_srtp_transport") { + visibility = [ ":*" ] + sources = [ "dtls_srtp_transport.cc", "dtls_srtp_transport.h", + ] + deps = [ + ":srtp_transport", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("dtls_transport") { + visibility = [ ":*" ] + sources = [ "dtls_transport.cc", "dtls_transport.h", + ] + deps = [ + ":ice_transport", + "../api:libjingle_peerconnection_api", + "../api:scoped_refptr", + "../api:sequence_checker", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:threading", + "../rtc_base/synchronization:mutex", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("external_hmac") { + visibility = [ ":*" ] + sources = [ "external_hmac.cc", "external_hmac.h", + ] + deps = [ + "../rtc_base:logging", + "../rtc_base:zero_memory", + ] + if (rtc_build_libsrtp) { + deps += [ "//third_party/libsrtp" ] + } +} + +rtc_source_set("ice_transport") { + visibility = [ ":*" ] + sources = [ "ice_transport.cc", "ice_transport.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../api:sequence_checker", + "../rtc_base:checks", + "../rtc_base:macromagic", + "../rtc_base:threading", + ] +} + +rtc_source_set("jsep_transport") { + visibility = [ ":*" ] + sources = [ "jsep_transport.cc", "jsep_transport.h", + ] + deps = [ + ":dtls_srtp_transport", + ":dtls_transport", + ":rtcp_mux_filter", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_data_channel_transport", + ":sctp_transport", + ":session_description", + ":srtp_filter", + ":srtp_transport", + ":transport_stats", + "../api:array_view", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/transport:datagram_transport_interface", + "../media:rtc_data_sctp_transport_internal", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:stringutils", + "../rtc_base:threading", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("jsep_transport_collection") { + visibility = [ ":*" ] + sources = [ "jsep_transport_collection.cc", "jsep_transport_collection.h", + ] + deps = [ + ":jsep_transport", + ":session_description", + "../api:libjingle_peerconnection_api", + "../api:sequence_checker", + "../p2p:rtc_p2p", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base/system:no_unique_address", + ] + absl_deps = [] +} + +rtc_source_set("jsep_transport_controller") { + visibility = [ + ":*", + "../test/peer_scenario:*", + ] + sources = [ "jsep_transport_controller.cc", "jsep_transport_controller.h", + ] + deps = [ + ":channel", + ":dtls_srtp_transport", + ":dtls_transport", + ":jsep_transport", + ":jsep_transport_collection", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_transport", + ":session_description", + ":srtp_transport", + ":transport_stats", + "../api:async_dns_resolver", + "../api:ice_transport_factory", + "../api:libjingle_peerconnection_api", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/crypto:options", + "../api/rtc_event_log", + "../api/transport:datagram_transport_interface", + "../api/transport:enums", + "../api/transport:sctp_transport_factory_interface", + "../media:rtc_data_sctp_transport_internal", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:callback_list", + "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", + "../rtc_base:threading", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("media_session") { + visibility = [ "*" ] # Used by Chrome + sources = [ "media_session.cc", "media_session.h", - "media_stream_proxy.h", - "media_stream_track_proxy.h", - "peer_connection_factory_proxy.h", - "peer_connection_proxy.h", + ] + deps = [ + ":channel_manager", + ":jsep_transport", + ":media_protocol_names", + ":rtp_media_utils", + ":session_description", + ":simulcast_description", + ":used_ids", + "../api:field_trials_view", + "../api:libjingle_peerconnection_api", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api/crypto:options", + "../media:rtc_data_sctp_transport_internal", + "../media:rtc_media_base", + "../media:rtc_sdp_video_format_utils", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:stringutils", + "../rtc_base/third_party/base64", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("media_stream_proxy") { + visibility = [ ":*" ] + sources = [ "media_stream_proxy.h" ] + deps = [ + ":proxy", + "../api:media_stream_interface", + ] +} + +rtc_source_set("media_stream_track_proxy") { + visibility = [ ":*" ] + sources = [ "media_stream_track_proxy.h" ] + deps = [ + ":proxy", + "../api:media_stream_interface", + ] +} + +rtc_source_set("peer_connection_factory_proxy") { + visibility = [ ":*" ] + sources = [ "peer_connection_factory_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} + +rtc_source_set("peer_connection_proxy") { + visibility = [ ":*" ] + sources = [ "peer_connection_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} + +rtc_source_set("rtcp_mux_filter") { + visibility = [ ":*" ] + sources = [ "rtcp_mux_filter.cc", "rtcp_mux_filter.h", + ] + deps = [ + ":session_description", + "../rtc_base:logging", + ] +} + +rtc_source_set("rtp_media_utils") { + visibility = [ ":*" ] + sources = [ "rtp_media_utils.cc", "rtp_media_utils.h", - "rtp_receiver_proxy.h", - "rtp_sender_proxy.h", + ] + deps = [ + "../api:rtp_transceiver_direction", + "../rtc_base:checks", + ] +} + +rtc_source_set("rtp_receiver_proxy") { + visibility = [ ":*" ] + sources = [ "rtp_receiver_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} + +rtc_source_set("rtp_sender_proxy") { + visibility = [ ":*" ] + sources = [ "rtp_sender_proxy.h" ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + ] +} + +rtc_source_set("rtp_transport") { + visibility = [ ":*" ] + sources = [ "rtp_transport.cc", "rtp_transport.h", - "rtp_transport_internal.h", + ] + deps = [ + ":rtp_transport_internal", + ":session_description", + "../api:array_view", + "../api/units:timestamp", + "../call:rtp_receiver", + "../call:video_stream_api", + "../media:rtc_media_base", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:logging", + "../rtc_base:socket", + "../rtc_base/network:sent_packet", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("rtp_transport_internal") { + visibility = [ + ":*", + "../test/peer_scenario", + ] + sources = [ "rtp_transport_internal.h" ] + deps = [ + ":session_description", + "../call:rtp_receiver", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base/third_party/sigslot", + ] +} + +rtc_source_set("sctp_data_channel_transport") { + visibility = [ ":*" ] + sources = [ "sctp_data_channel_transport.cc", "sctp_data_channel_transport.h", + ] + deps = [ + "../api:rtc_error", + "../api/transport:datagram_transport_interface", + "../media:rtc_data_sctp_transport_internal", + "../media:rtc_media_base", + "../rtc_base:copy_on_write_buffer", + "../rtc_base/third_party/sigslot", + ] +} + +rtc_source_set("sctp_transport") { + visibility = [ ":*" ] + sources = [ "sctp_transport.cc", "sctp_transport.h", + ] + deps = [ + ":dtls_transport", + "../api:libjingle_peerconnection_api", + "../api:scoped_refptr", + "../api:sequence_checker", + "../media:rtc_data_sctp_transport_internal", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:checks", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:threading", + "../rtc_base/third_party/sigslot", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("sctp_utils") { + visibility = [ + ":*", + "../test/fuzzers:*", + ] + sources = [ "sctp_utils.cc", "sctp_utils.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../api:priority", + "../api/transport:datagram_transport_interface", + "../media:rtc_media_base", + "../rtc_base:byte_buffer", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:logging", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} +rtc_source_set("srtp_filter") { + visibility = [ ":*" ] + sources = [ "srtp_filter.cc", "srtp_filter.h", + ] + deps = [ + ":session_description", + "../api:array_view", + "../api:libjingle_peerconnection_api", + "../api:sequence_checker", + "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:logging", + "../rtc_base:zero_memory", + "../rtc_base/third_party/base64", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_source_set("srtp_session") { + visibility = [ ":*" ] + sources = [ "srtp_session.cc", "srtp_session.h", + ] + deps = [ + ":external_hmac", + "../api:array_view", + "../api:field_trials_view", + "../api:scoped_refptr", + "../api:sequence_checker", + "../modules/rtp_rtcp:rtp_rtcp_format", + "../rtc_base", + "../rtc_base:byte_order", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:stringutils", + "../rtc_base:timeutils", + "../rtc_base/synchronization:mutex", + "../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/strings:strings", + ] + if (rtc_build_libsrtp) { + deps += [ "//third_party/libsrtp" ] + } +} +rtc_source_set("srtp_transport") { + visibility = [ ":*" ] + sources = [ "srtp_transport.cc", "srtp_transport.h", - "transport_stats.cc", - "transport_stats.h", - "used_ids.h", - "video_track_source_proxy.cc", - "video_track_source_proxy.h", ] - deps = [ - ":media_protocol_names", - ":proxy", - ":session_description", - ":simulcast_description", - "../api:array_view", - "../api:async_dns_resolver", - "../api:audio_options_api", - "../api:call_api", - "../api:function_view", - "../api:ice_transport_factory", + ":rtp_transport", + ":srtp_session", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", - "../api:media_stream_interface", - "../api:packet_socket_factory", - "../api:priority", "../api:rtc_error", - "../api:rtp_headers", - "../api:rtp_parameters", - "../api:rtp_parameters", - "../api:rtp_transceiver_direction", - "../api:scoped_refptr", - "../api:sequence_checker", - "../api/crypto:options", - "../api/rtc_event_log", - "../api/task_queue", - "../api/transport:datagram_transport_interface", - "../api/transport:enums", - "../api/transport:sctp_transport_factory_interface", - "../api/video:builtin_video_bitrate_allocator_factory", - "../api/video:video_bitrate_allocator_factory", - "../api/video:video_frame", - "../api/video:video_rtp_headers", - "../api/video_codecs:video_codecs_api", - "../call:call_interfaces", - "../call:rtp_interfaces", - "../call:rtp_receiver", - "../common_video", - "../common_video:common_video", - "../logging:ice_log", - "../media:rtc_data_sctp_transport_internal", "../media:rtc_media_base", - "../media:rtc_media_config", - "../media:rtc_sdp_video_format_utils", - "../modules/rtp_rtcp:rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../p2p:rtc_p2p", "../rtc_base", - "../rtc_base:callback_list", + "../rtc_base:buffer", "../rtc_base:checks", - "../rtc_base:rtc_task_queue", - "../rtc_base:socket", - "../rtc_base:socket_address", - "../rtc_base:stringutils", - "../rtc_base:threading", - "../rtc_base/network:sent_packet", - "../rtc_base/synchronization:mutex", - "../rtc_base/system:file_wrapper", - "../rtc_base/system:no_unique_address", - "../rtc_base/system:rtc_export", - "../rtc_base/task_utils:pending_task_safety_flag", - "../rtc_base/task_utils:to_queued_task", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", + "../rtc_base:logging", + "../rtc_base:safe_conversions", + "../rtc_base:zero_memory", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", - "../system_wrappers:field_trial", - "../system_wrappers:metrics", ] 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/strings", "//third_party/abseil-cpp/absl/types:optional", ] +} + +rtc_source_set("transport_stats") { + visibility = [ ":*" ] + sources = [ + "transport_stats.cc", + "transport_stats.h", + ] + deps = [ + "../api:libjingle_peerconnection_api", + "../p2p:rtc_p2p", + "../rtc_base", + ] +} - if (rtc_build_libsrtp) { - deps += [ "//third_party/libsrtp" ] - } +rtc_source_set("used_ids") { + visibility = [ ":*" ] + sources = [ "used_ids.h" ] + deps = [ + "../api:rtp_parameters", + "../media:rtc_media_base", + "../rtc_base:checks", + "../rtc_base:logging", + ] +} - public_configs = [ ":rtc_pc_config" ] +rtc_source_set("video_track_source_proxy") { + visibility = [ "*" ] # Used by Chrome + sources = [ + "video_track_source_proxy.cc", + "video_track_source_proxy.h", + ] + deps = [ + ":proxy", + "../api:libjingle_peerconnection_api", + "../api:media_stream_interface", + "../api:scoped_refptr", + "../api:video_track_source_constraints", + "../api/video:recordable_encoded_frame", + "../api/video:video_frame", + "../rtc_base:threading", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_source_set("session_description") { @@ -219,6 +717,7 @@ rtc_source_set("session_description") { "../p2p:rtc_p2p", "../rtc_base:checks", "../rtc_base:socket_address", + "../rtc_base:stringutils", "../rtc_base/system:rtc_export", ] absl_deps = [ @@ -245,10 +744,7 @@ rtc_source_set("rtc_pc") { visibility = [ "*" ] } allow_poison = [ "audio_codecs" ] # TODO(bugs.webrtc.org/8396): Remove. - deps = [ - ":rtc_pc_base", - "../media:rtc_audio_video", - ] + deps = [ "../media:rtc_audio_video" ] } rtc_library("media_protocol_names") { @@ -287,7 +783,6 @@ rtc_source_set("peerconnection") { ":peer_connection_message_handler", ":proxy", ":remote_audio_source", - ":rtc_pc_base", ":rtc_stats_collector", ":rtc_stats_traversal", ":rtp_parameters_conversion", @@ -319,6 +814,7 @@ rtc_source_set("peerconnection") { "../api:call_api", "../api:callfactory_api", "../api:fec_controller_api", + "../api:field_trials_view", "../api:frame_transformer_interface", "../api:ice_transport_factory", "../api:libjingle_logging_api", @@ -347,7 +843,6 @@ rtc_source_set("peerconnection") { "../api/transport:field_trial_based_config", "../api/transport:network_control", "../api/transport:sctp_transport_factory_interface", - "../api/transport:webrtc_key_value_config", "../api/units:data_rate", "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:video_bitrate_allocator_factory", @@ -371,7 +866,6 @@ rtc_source_set("peerconnection") { "../rtc_base:checks", "../rtc_base:ip_address", "../rtc_base:network_constants", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_operations_chain", "../rtc_base:safe_minmax", "../rtc_base:socket_address", @@ -409,7 +903,7 @@ rtc_library("sctp_data_channel") { deps = [ ":data_channel_utils", ":proxy", - ":rtc_pc_base", + ":sctp_utils", "../api:libjingle_peerconnection_api", "../api:priority", "../api:rtc_error", @@ -418,12 +912,12 @@ rtc_library("sctp_data_channel") { "../media:rtc_data_sctp_transport_internal", "../media:rtc_media_base", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:refcount", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", - "../rtc_base:rtc_base_approved", "../rtc_base:threading", "../rtc_base:threading", "../rtc_base/system:unused", @@ -455,8 +949,9 @@ rtc_library("connection_context") { "connection_context.h", ] deps = [ - ":rtc_pc_base", + ":channel_manager", "../api:callfactory_api", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:refcountedbase", @@ -465,13 +960,17 @@ rtc_library("connection_context") { "../api/neteq:neteq_api", "../api/transport:field_trial_based_config", "../api/transport:sctp_transport_factory_interface", - "../api/transport:webrtc_key_value_config", "../media:rtc_data_sctp_transport_factory", "../media:rtc_media_base", "../p2p:rtc_p2p", "../rtc_base", "../rtc_base:checks", + "../rtc_base:macromagic", + "../rtc_base:socket_factory", + "../rtc_base:socket_server", "../rtc_base:threading", + "../rtc_base:timeutils", + "../rtc_base/memory:always_valid_pointer", "../rtc_base/task_utils:to_queued_task", ] } @@ -485,23 +984,24 @@ rtc_source_set("data_channel_controller") { deps = [ ":data_channel_utils", ":peer_connection_internal", - ":rtc_pc_base", ":sctp_data_channel", + ":sctp_utils", "../api:libjingle_peerconnection_api", "../api:rtc_error", "../api:scoped_refptr", "../api:sequence_checker", "../api/transport:datagram_transport_interface", "../media:rtc_media_base", + "../rtc_base", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", - "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:threading", "../rtc_base:weak_ptr", "../rtc_base/task_utils:to_queued_task", - "../rtc_base/third_party/sigslot:sigslot", + "../rtc_base/third_party/sigslot", ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", @@ -509,12 +1009,26 @@ rtc_source_set("data_channel_controller") { ] } +rtc_source_set("peer_connection_sdp_methods") { + visibility = [ ":*" ] + sources = [ "peer_connection_sdp_methods.h" ] + deps = [ + ":jsep_transport_controller", + ":peer_connection_message_handler", + ":sctp_data_channel", + ":usage_pattern", + "../api:libjingle_peerconnection_api", + "../call:call_interfaces", + ] +} + rtc_source_set("peer_connection_internal") { visibility = [ ":*" ] sources = [ "peer_connection_internal.h" ] deps = [ + ":jsep_transport_controller", ":peer_connection_message_handler", - ":rtc_pc_base", + ":peer_connection_sdp_methods", ":rtp_transceiver", ":rtp_transmission_manager", ":sctp_data_channel", @@ -522,22 +1036,30 @@ rtc_source_set("peer_connection_internal") { "../call:call_interfaces", ] } + rtc_source_set("rtc_stats_collector") { - visibility = [ ":*" ] + visibility = [ + ":*", + "../api:*", + ] sources = [ "rtc_stats_collector.cc", "rtc_stats_collector.h", ] deps = [ + ":channel", + ":channel_interface", ":data_channel_utils", ":peer_connection_internal", - ":rtc_pc_base", ":rtc_stats_traversal", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", ":sctp_data_channel", ":track_media_info_map", + ":transport_stats", ":webrtc_sdp", "../api:array_view", "../api:libjingle_peerconnection_api", @@ -547,6 +1069,7 @@ rtc_source_set("rtc_stats_collector") { "../api:scoped_refptr", "../api:sequence_checker", "../api/task_queue:task_queue", + "../api/units:time_delta", "../api/video:video_rtp_headers", "../call:call_interfaces", "../common_video:common_video", @@ -555,12 +1078,13 @@ rtc_source_set("rtc_stats_collector") { "../modules/rtp_rtcp:rtp_rtcp_format", "../p2p:rtc_p2p", "../rtc_base:checks", + "../rtc_base:event_tracer", "../rtc_base:ip_address", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:network_constants", "../rtc_base:refcount", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_event", "../rtc_base:socket_address", "../rtc_base:stringutils", @@ -568,7 +1092,10 @@ rtc_source_set("rtc_stats_collector") { "../rtc_base:timeutils", "../rtc_base/third_party/sigslot:sigslot", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_source_set("rtc_stats_traversal") { @@ -591,18 +1118,24 @@ rtc_source_set("sdp_offer_answer") { "sdp_offer_answer.h", # dependent on peerconnection.h ] deps = [ + ":channel", + ":channel_interface", + ":channel_manager", ":connection_context", ":data_channel_controller", - ":data_channel_utils", - ":ice_server_parsing", + ":dtls_transport", + ":jsep_transport_controller", + ":media_session", ":media_stream", ":media_stream_observer", + ":media_stream_proxy", ":peer_connection_internal", ":peer_connection_message_handler", - ":rtc_pc_base", - ":rtc_stats_collector", + ":rtp_media_utils", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", ":rtp_transmission_manager", ":sdp_state_provider", @@ -615,6 +1148,7 @@ rtc_source_set("sdp_offer_answer") { ":webrtc_session_description_factory", "../api:array_view", "../api:audio_options_api", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:rtc_error", @@ -623,24 +1157,22 @@ rtc_source_set("sdp_offer_answer") { "../api:scoped_refptr", "../api:sequence_checker", "../api/crypto:options", - "../api/transport:datagram_transport_interface", "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:video_bitrate_allocator_factory", "../media:rtc_media_base", "../p2p:rtc_p2p", + "../rtc_base", "../rtc_base:checks", + "../rtc_base:event_tracer", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:refcount", - "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_operations_chain", "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base:weak_ptr", - "../rtc_base/experiments:field_trial_parser", - "../rtc_base/third_party/sigslot:sigslot", - "../system_wrappers:field_trial", + "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", ] absl_deps = [ @@ -676,33 +1208,40 @@ rtc_source_set("peer_connection") { "peer_connection.h", ] deps = [ + ":channel", + ":channel_interface", + ":channel_manager", ":connection_context", ":data_channel_controller", ":data_channel_utils", + ":dtls_transport", ":ice_server_parsing", + ":jsep_transport_controller", ":peer_connection_internal", ":peer_connection_message_handler", - ":rtc_pc_base", ":rtc_stats_collector", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", ":rtp_transmission_manager", + ":rtp_transport_internal", ":sctp_data_channel", + ":sctp_transport", ":sdp_offer_answer", ":session_description", ":simulcast_description", ":stats_collector", - ":stream_collection", ":transceiver_list", + ":transport_stats", ":usage_pattern", ":webrtc_session_description_factory", "../api:async_dns_resolver", - "../api:audio_options_api", + "../api:field_trials_view", "../api:libjingle_logging_api", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", - "../api:packet_socket_factory", "../api:rtc_error", "../api:rtc_stats_api", "../api:rtp_parameters", @@ -711,34 +1250,33 @@ rtc_source_set("peer_connection") { "../api:sequence_checker", "../api/adaptation:resource_adaptation_api", "../api/crypto:options", - "../api/rtc_event_log:rtc_event_log", + "../api/rtc_event_log", "../api/transport:bitrate_settings", "../api/transport:datagram_transport_interface", "../api/transport:enums", - "../api/transport:webrtc_key_value_config", - "../api/video:video_bitrate_allocator_factory", "../api/video:video_codec_constants", "../call:call_interfaces", "../media:rtc_media_base", "../media:rtc_media_config", "../modules/rtp_rtcp:rtp_rtcp_format", "../p2p:rtc_p2p", + "../rtc_base", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", "../rtc_base:ip_address", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:network_constants", "../rtc_base:refcount", - "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:socket_address", "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base:weak_ptr", - "../rtc_base/network:sent_packet", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", - "../rtc_base/third_party/sigslot:sigslot", + "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", ] absl_deps = [ @@ -789,12 +1327,17 @@ rtc_source_set("stats_collector") { "stats_collector.h", ] deps = [ + ":channel", + ":channel_interface", ":data_channel_utils", ":peer_connection_internal", - ":rtc_pc_base", ":rtp_receiver", + ":rtp_receiver_proxy", + ":rtp_sender_proxy", ":rtp_transceiver", ":stats_collector_interface", + ":transport_stats", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:rtp_parameters", @@ -807,18 +1350,22 @@ rtc_source_set("stats_collector") { "../modules/audio_processing:audio_processing_statistics", "../p2p:rtc_p2p", "../rtc_base:checks", + "../rtc_base:event_tracer", "../rtc_base:ip_address", + "../rtc_base:location", "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:network_constants", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:socket_address", "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base:timeutils", - "../system_wrappers:field_trial", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_source_set("stream_collection") { visibility = [ ":*" ] @@ -856,7 +1403,7 @@ rtc_source_set("webrtc_sdp") { ] deps = [ ":media_protocol_names", - ":rtc_pc_base", + ":media_session", ":sdp_serializer", ":session_description", ":simulcast_description", @@ -879,6 +1426,7 @@ rtc_source_set("webrtc_sdp") { ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] } @@ -889,18 +1437,21 @@ rtc_source_set("webrtc_session_description_factory") { "webrtc_session_description_factory.h", ] deps = [ - ":rtc_pc_base", + ":channel_manager", + ":connection_context", + ":media_session", ":sdp_state_provider", ":session_description", "../api:libjingle_peerconnection_api", "../api:rtc_error", "../api:scoped_refptr", + "../api:sequence_checker", "../p2p:rtc_p2p", "../rtc_base:checks", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:refcount", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:stringutils", "../rtc_base:threading", "../rtc_base/third_party/sigslot:sigslot", @@ -952,11 +1503,17 @@ rtc_source_set("peer_connection_factory") { "peer_connection_factory.h", ] deps = [ + ":channel_manager", ":local_audio_source", + ":media_stream_proxy", + ":media_stream_track_proxy", ":peer_connection", + ":peer_connection_factory_proxy", + ":peer_connection_proxy", "../api:audio_options_api", "../api:callfactory_api", "../api:fec_controller_api", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:network_state_predictor_api", @@ -965,13 +1522,13 @@ rtc_source_set("peer_connection_factory") { "../api:rtp_parameters", "../api:scoped_refptr", "../api:sequence_checker", + "../api/metronome", "../api/neteq:neteq_api", "../api/rtc_event_log:rtc_event_log", "../api/task_queue:task_queue", "../api/transport:bitrate_settings", "../api/transport:network_control", "../api/transport:sctp_transport_factory_interface", - "../api/transport:webrtc_key_value_config", "../api/units:data_rate", "../call:call_interfaces", "../call:rtp_interfaces", @@ -981,16 +1538,15 @@ rtc_source_set("peer_connection_factory") { "../pc:audio_track", "../pc:connection_context", "../pc:media_stream", - "../pc:rtc_pc_base", "../pc:rtp_parameters_conversion", "../pc:session_description", "../pc:video_track", "../rtc_base:checks", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:refcount", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:safe_conversions", "../rtc_base:threading", "../rtc_base/experiments:field_trial_parser", @@ -1014,6 +1570,7 @@ rtc_library("peer_connection_message_handler") { "../api:sequence_checker", "../rtc_base", "../rtc_base:checks", + "../rtc_base:location", "../rtc_base:threading", ] } @@ -1038,13 +1595,21 @@ rtc_library("rtp_transceiver") { "rtp_transceiver.h", ] deps = [ + ":channel", + ":channel_interface", + ":channel_manager", + ":peer_connection_sdp_methods", ":proxy", - ":rtc_pc_base", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", + ":rtp_transport_internal", ":session_description", "../api:array_view", + "../api:audio_options_api", "../api:libjingle_peerconnection_api", "../api:rtc_error", "../api:rtp_parameters", @@ -1052,8 +1617,10 @@ rtc_library("rtp_transceiver") { "../api:scoped_refptr", "../api:sequence_checker", "../api/task_queue", + "../api/video:video_bitrate_allocator_factory", "../media:rtc_media_base", "../rtc_base:checks", + "../rtc_base:location", "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:refcount", @@ -1077,9 +1644,13 @@ rtc_library("rtp_transmission_manager") { ] deps = [ ":audio_rtp_receiver", - ":rtc_pc_base", + ":channel", + ":channel_interface", + ":channel_manager", ":rtp_receiver", + ":rtp_receiver_proxy", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", ":stats_collector_interface", ":transceiver_list", @@ -1095,6 +1666,9 @@ rtc_library("rtp_transmission_manager") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:threading", "../rtc_base:weak_ptr", "../rtc_base/third_party/sigslot", @@ -1134,7 +1708,7 @@ rtc_library("rtp_receiver") { ] deps = [ ":media_stream", - ":rtc_pc_base", + ":media_stream_proxy", ":video_track_source", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", @@ -1145,8 +1719,8 @@ rtc_library("rtp_receiver") { "../media:rtc_media_base", "../rtc_base:checks", "../rtc_base:logging", + "../rtc_base:refcount", "../rtc_base:rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:threading", ] absl_deps = [ @@ -1166,8 +1740,8 @@ rtc_library("audio_rtp_receiver") { ":audio_track", ":jitter_buffer_delay", ":media_stream", + ":media_stream_track_proxy", ":remote_audio_source", - ":rtc_pc_base", ":rtp_receiver", "../api:frame_transformer_interface", "../api:libjingle_peerconnection_api", @@ -1180,6 +1754,8 @@ rtc_library("audio_rtp_receiver") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", + "../rtc_base:location", + "../rtc_base:macromagic", "../rtc_base:refcount", "../rtc_base:threading", "../rtc_base/system:no_unique_address", @@ -1202,7 +1778,7 @@ rtc_library("video_rtp_receiver") { deps = [ ":jitter_buffer_delay", ":media_stream", - ":rtc_pc_base", + ":media_stream_track_proxy", ":rtp_receiver", ":video_rtp_track_source", ":video_track", @@ -1219,7 +1795,10 @@ rtc_library("video_rtp_receiver") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:threading", "../rtc_base/system:no_unique_address", ] @@ -1244,6 +1823,7 @@ rtc_library("video_rtp_track_source") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", + "../rtc_base:macromagic", "../rtc_base/synchronization:mutex", "../rtc_base/system:no_unique_address", ] @@ -1272,7 +1852,7 @@ rtc_library("video_track") { "video_track.h", ] deps = [ - ":rtc_pc_base", + ":video_track_source_proxy", "../api:media_stream_interface", "../api:scoped_refptr", "../api:sequence_checker", @@ -1280,20 +1860,19 @@ rtc_library("video_track") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", + "../rtc_base:location", + "../rtc_base:macromagic", "../rtc_base:refcount", - "../rtc_base:rtc_base_approved", "../rtc_base:threading", "../rtc_base/system:no_unique_address", ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_source_set("sdp_state_provider") { visibility = [ ":*" ] sources = [ "sdp_state_provider.h" ] - deps = [ - ":rtc_pc_base", - "../api:libjingle_peerconnection_api", - ] + deps = [ "../api:libjingle_peerconnection_api" ] } rtc_library("jitter_buffer_delay") { @@ -1305,6 +1884,7 @@ rtc_library("jitter_buffer_delay") { deps = [ "../api:sequence_checker", "../rtc_base:checks", + "../rtc_base:macromagic", "../rtc_base:safe_conversions", "../rtc_base:safe_minmax", "../rtc_base/system:no_unique_address", @@ -1319,7 +1899,7 @@ rtc_library("remote_audio_source") { "remote_audio_source.h", ] deps = [ - ":rtc_pc_base", + ":channel", "../api:call_api", "../api:media_stream_interface", "../api:scoped_refptr", @@ -1327,8 +1907,8 @@ rtc_library("remote_audio_source") { "../media:rtc_media_base", "../rtc_base", "../rtc_base:checks", + "../rtc_base:location", "../rtc_base:logging", - "../rtc_base:rtc_base_approved", "../rtc_base:safe_conversions", "../rtc_base:stringutils", "../rtc_base:threading", @@ -1358,9 +1938,15 @@ rtc_library("rtp_sender") { "../api:rtc_error", "../api:rtp_parameters", "../api:scoped_refptr", + "../api:sequence_checker", "../api/crypto:frame_encryptor_interface", "../media:rtc_media_base", "../rtc_base:checks", + "../rtc_base:event_tracer", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_base", "../rtc_base:threading", "../rtc_base/synchronization:mutex", @@ -1380,7 +1966,6 @@ rtc_library("rtp_parameters_conversion") { "rtp_parameters_conversion.h", ] deps = [ - ":rtc_pc_base", ":session_description", "../api:array_view", "../api:libjingle_peerconnection_api", @@ -1388,7 +1973,9 @@ rtc_library("rtp_parameters_conversion") { "../api:rtp_parameters", "../media:rtc_media_base", "../rtc_base:checks", + "../rtc_base:logging", "../rtc_base:rtc_base", + "../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", @@ -1407,7 +1994,12 @@ rtc_library("dtmf_sender") { ":proxy", "../api:libjingle_peerconnection_api", "../api:scoped_refptr", + "../api:sequence_checker", "../rtc_base:checks", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_base", "../rtc_base:threading", "../rtc_base/task_utils:pending_task_safety_flag", @@ -1454,7 +2046,7 @@ rtc_library("video_track_source") { "../api/video:video_frame", "../media:rtc_media_base", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:macromagic", "../rtc_base/system:no_unique_address", "../rtc_base/system:rtc_export", ] @@ -1515,12 +2107,29 @@ if (rtc_include_tests && !build_with_chromium) { deps = [ ":audio_rtp_receiver", + ":channel", + ":channel_manager", + ":dtls_srtp_transport", + ":dtls_transport", + ":ice_transport", + ":jsep_transport", + ":jsep_transport_controller", ":libjingle_peerconnection", + ":media_protocol_names", + ":media_session", ":pc_test_utils", ":peerconnection", ":rtc_pc", - ":rtc_pc_base", + ":rtcp_mux_filter", + ":rtp_media_utils", + ":rtp_transport", + ":rtp_transport_internal", + ":sctp_transport", ":session_description", + ":srtp_filter", + ":srtp_session", + ":srtp_transport", + ":used_ids", ":video_rtp_receiver", "../api:array_view", "../api:audio_options_api", @@ -1529,7 +2138,13 @@ if (rtc_include_tests && !build_with_chromium) { "../api:rtc_error", "../api:rtp_headers", "../api:rtp_parameters", + "../api:scoped_refptr", + "../api:sequence_checker", + "../api/task_queue:task_queue", + "../api/transport:datagram_transport_interface", + "../api/transport:enums", "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:recordable_encoded_frame", "../api/video/test:mock_recordable_encoded_frame", "../call:rtp_interfaces", "../call:rtp_receiver", @@ -1542,22 +2157,33 @@ if (rtc_include_tests && !build_with_chromium) { "../p2p:p2p_test_utils", "../p2p:rtc_p2p", "../rtc_base", + "../rtc_base:buffer", + "../rtc_base:byte_order", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:socket_address", + "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base/containers:flat_set", "../rtc_base/task_utils:pending_task_safety_flag", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", - "../test:field_trial", + "../test:explicit_key_value_config", + "../test:scoped_key_value_config", "../test:test_common", "../test:test_main", "../test:test_support", "//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", ] if (rtc_build_libsrtp) { @@ -1565,7 +2191,12 @@ if (rtc_include_tests && !build_with_chromium) { } if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] + use_default_launcher = false + deps += [ + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/native_test:native_test_java", + "//testing/android/native_test:native_test_support", + ] } } @@ -1581,6 +2212,7 @@ if (rtc_include_tests && !build_with_chromium) { "../api:create_peerconnection_factory", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtc_stats_api", "../api:scoped_refptr", "../api/audio:audio_mixer_api", @@ -1598,8 +2230,11 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base", "../rtc_base:checks", "../rtc_base:gunit_helpers", + "../rtc_base:location", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", "../rtc_base:socket_address", + "../rtc_base:socket_factory", "../rtc_base:threading", "../system_wrappers", "../test:perf_test", @@ -1627,8 +2262,34 @@ if (rtc_include_tests && !build_with_chromium) { "../api:scoped_refptr", "../rtc_base:checks", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:refcount", + "../test:test_support", + ] + } + + rtc_test("slow_peer_connection_unittests") { + testonly = true + sources = [ "slow_peer_connection_integration_test.cc" ] + deps = [ + ":integration_test_helpers", + ":pc_test_utils", + "../api:libjingle_peerconnection_api", + "../api:scoped_refptr", + "../api/units:time_delta", + "../p2p:p2p_server_utils", + "../p2p:p2p_test_utils", + "../p2p:rtc_p2p", + "../rtc_base", + "../rtc_base:gunit_helpers", + "../rtc_base:logging", + "../rtc_base:rtc_base_tests_utils", + "../rtc_base:socket_address", + "../test:test_main", "../test:test_support", + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", ] } @@ -1684,32 +2345,44 @@ if (rtc_include_tests && !build_with_chromium) { deps = [ ":audio_rtp_receiver", ":audio_track", + ":channel", + ":channel_interface", + ":channel_manager", + ":data_channel_controller_unittest", + ":dtls_srtp_transport", + ":dtls_transport", ":dtmf_sender", ":ice_server_parsing", ":integration_test_helpers", ":jitter_buffer_delay", ":local_audio_source", + ":media_protocol_names", + ":media_session", ":media_stream", ":peer_connection", ":peer_connection_factory", - ":peer_connection_factory", - ":peerconnection", + ":peer_connection_proxy", ":proxy", - ":remote_audio_source", - ":rtc_pc_base", ":rtc_stats_collector", ":rtc_stats_traversal", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", ":rtp_sender", + ":rtp_sender_proxy", ":rtp_transceiver", + ":rtp_transport_internal", ":sctp_data_channel", + ":sctp_transport", + ":sctp_utils", ":sdp_serializer", ":sdp_utils", ":session_description", + ":simulcast_description", ":stats_collector", ":stream_collection", ":track_media_info_map", + ":transport_stats", ":usage_pattern", ":video_rtp_receiver", ":video_rtp_track_source", @@ -1721,16 +2394,18 @@ if (rtc_include_tests && !build_with_chromium) { "../api:create_peerconnection_factory", "../api:fake_frame_decryptor", "../api:fake_frame_encryptor", + "../api:field_trials_view", "../api:function_view", "../api:libjingle_logging_api", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", - "../api:mock_rtp", "../api:mock_video_track", "../api:packet_socket_factory", + "../api:priority", "../api:rtc_error", "../api:rtp_transceiver_direction", "../api:scoped_refptr", + "../api/adaptation:resource_adaptation_api", "../api/audio:audio_mixer_api", "../api/crypto:frame_decryptor_interface", "../api/crypto:frame_encryptor_interface", @@ -1739,49 +2414,71 @@ if (rtc_include_tests && !build_with_chromium) { "../api/rtc_event_log:rtc_event_log_factory", "../api/task_queue", "../api/task_queue:default_task_queue_factory", + "../api/transport:datagram_transport_interface", "../api/transport:field_trial_based_config", - "../api/transport:webrtc_key_value_config", + "../api/transport:sctp_transport_factory_interface", "../api/transport/rtp:rtp_source", "../api/units:time_delta", + "../api/units:timestamp", "../api/video:builtin_video_bitrate_allocator_factory", + "../api/video:encoded_image", + "../api/video:recordable_encoded_frame", + "../api/video:video_bitrate_allocator_factory", + "../api/video:video_codec_constants", + "../api/video:video_frame", "../api/video:video_rtp_headers", "../call/adaptation:resource_adaptation_test_utilities", + "../common_video", "../logging:fake_rtc_event_log", "../media:rtc_data_sctp_transport_internal", "../media:rtc_media_config", "../media:rtc_media_engine_defaults", "../modules/audio_device:audio_device_api", "../modules/audio_processing:audio_processing_statistics", - "../modules/audio_processing:audioproc_test_utils", "../modules/rtp_rtcp:rtp_rtcp_format", - "../p2p:fake_ice_transport", "../p2p:fake_port_allocator", "../p2p:p2p_server_utils", + "../rtc_base:byte_buffer", "../rtc_base:checks", + "../rtc_base:copy_on_write_buffer", + "../rtc_base:event_tracer", "../rtc_base:gunit_helpers", "../rtc_base:ip_address", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:network_constants", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", "../rtc_base:rtc_json", "../rtc_base:socket_address", + "../rtc_base:stringutils", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", - "../system_wrappers:field_trial", "../system_wrappers:metrics", - "../test:field_trial", - "../test:fileutils", - "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_common", "../test/pc/sctp:fake_sctp_transport", - "./scenario_tests:pc_scenario_tests", "//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", ] + if (is_android) { - deps += [ ":android_black_magic" ] + use_default_launcher = false + deps += [ + ":android_black_magic", + + # We need to depend on this one directly, or classloads will fail for + # the voice engine BuildInfo, for instance. + "//sdk/android:libjingle_peerconnection_java", + "//sdk/android:native_test_jni_onload", + ] + shard_timeout = 900 } deps += [ @@ -1812,40 +2509,41 @@ if (rtc_include_tests && !build_with_chromium) { "../p2p:p2p_test_utils", "../p2p:rtc_p2p", "../rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_task_queue", "../rtc_base:safe_conversions", "../test:audio_codec_mocks", "../test:test_main", "../test:test_support", ] + } - if (is_android) { - deps += [ - "//testing/android/native_test:native_test_support", - - # We need to depend on this one directly, or classloads will fail for - # the voice engine BuildInfo, for instance. - "../sdk/android:libjingle_peerconnection_java", - ] - - shard_timeout = 900 - } + rtc_library("data_channel_controller_unittest") { + testonly = true + sources = [ "data_channel_controller_unittest.cc" ] + deps = [ + ":data_channel_controller", + ":pc_test_utils", + ":peer_connection_internal", + ":sctp_data_channel", + "../test:test_support", + ] } if (is_android) { rtc_library("android_black_magic") { - # The android code uses hacky includes to chromium-base and the ssl code; - # having this in a separate target enables us to keep the peerconnection - # unit tests clean. - check_includes = false + # The android code uses hacky includes to ssl code. Having this in a + # separate target enables us to keep the peerconnection unit tests clean. testonly = true sources = [ "test/android_test_initializer.cc", "test/android_test_initializer.h", ] deps = [ + "../sdk/android:internal_jni", "../sdk/android:libjingle_peerconnection_jni", + "//modules/utility:utility", + "//rtc_base:checks", + "//rtc_base:rtc_base", "//testing/android/native_test:native_test_support", ] } @@ -1863,13 +2561,15 @@ if (rtc_include_tests && !build_with_chromium) { ":dtmf_sender", ":jitter_buffer_delay", ":local_audio_source", + ":media_session", ":media_stream", ":pc_test_utils", ":peer_connection", ":peer_connection_factory", + ":peer_connection_proxy", ":peerconnection", ":remote_audio_source", - ":rtc_pc_base", + ":rtp_media_utils", ":rtp_parameters_conversion", ":rtp_receiver", ":rtp_sender", @@ -1886,6 +2586,7 @@ if (rtc_include_tests && !build_with_chromium) { "../api:create_peerconnection_factory", "../api:fake_frame_decryptor", "../api:fake_frame_encryptor", + "../api:field_trials_view", "../api:function_view", "../api:libjingle_logging_api", "../api:libjingle_peerconnection_api", @@ -1906,7 +2607,6 @@ if (rtc_include_tests && !build_with_chromium) { "../api/task_queue", "../api/task_queue:default_task_queue_factory", "../api/transport:field_trial_based_config", - "../api/transport:webrtc_key_value_config", "../api/transport/rtp:rtp_source", "../api/units:time_delta", "../api/video:builtin_video_bitrate_allocator_factory", @@ -1934,20 +2634,28 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base:checks", "../rtc_base:gunit_helpers", "../rtc_base:ip_address", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_base_tests_utils", + "../rtc_base:rtc_event", "../rtc_base:rtc_json", + "../rtc_base:safe_conversions", "../rtc_base:socket_address", "../rtc_base:threading", "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:pending_task_safety_flag", + "../rtc_base/task_utils:repeating_task", "../rtc_base/task_utils:to_queued_task", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", - "../test:field_trial", + "../test:explicit_key_value_config", "../test:fileutils", "../test:rtp_test_utils", + "../test:scoped_key_value_config", "../test:test_support", "../test/pc/sctp:fake_sctp_transport", ] @@ -1964,7 +2672,7 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "test/fake_audio_capture_module.cc", "test/fake_audio_capture_module.h", - "test/fake_data_channel_provider.h", + "test/fake_data_channel_controller.h", "test/fake_peer_connection_base.h", "test/fake_peer_connection_for_stats.h", "test/fake_periodic_video_source.h", @@ -1975,6 +2683,7 @@ if (rtc_include_tests && !build_with_chromium) { "test/frame_generator_capturer_video_track_source.h", "test/mock_channel_interface.h", "test/mock_data_channel.h", + "test/mock_peer_connection_internal.h", "test/mock_peer_connection_observers.h", "test/mock_rtp_receiver_internal.h", "test/mock_rtp_sender_internal.h", @@ -1986,11 +2695,13 @@ if (rtc_include_tests && !build_with_chromium) { ] deps = [ + ":channel", + ":channel_interface", + ":channel_manager", ":jitter_buffer_delay", ":libjingle_peerconnection", ":peer_connection_internal", ":peerconnection", - ":rtc_pc_base", ":rtp_receiver", ":rtp_sender", ":sctp_data_channel", @@ -1999,6 +2710,8 @@ if (rtc_include_tests && !build_with_chromium) { "../api:audio_options_api", "../api:create_frame_generator", "../api:create_peerconnection_factory", + "../api:field_trials_view", + "../api:field_trials_view", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", "../api:rtc_error", @@ -2028,13 +2741,19 @@ if (rtc_include_tests && !build_with_chromium) { "../rtc_base", "../rtc_base:checks", "../rtc_base:gunit_helpers", - "../rtc_base:rtc_base_approved", + "../rtc_base:location", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:refcount", "../rtc_base:rtc_task_queue", + "../rtc_base:stringutils", "../rtc_base:task_queue_for_test", "../rtc_base:threading", + "../rtc_base:timeutils", "../rtc_base/synchronization:mutex", "../rtc_base/task_utils:repeating_task", "../rtc_base/third_party/sigslot", + "../test:scoped_key_value_config", "../test:test_support", "../test:video_test_common", ] diff --git a/pc/OWNERS b/pc/OWNERS index c194b50643..4f82de419e 100644 --- a/pc/OWNERS +++ b/pc/OWNERS @@ -3,3 +3,4 @@ hta@webrtc.org perkj@webrtc.org tommi@webrtc.org deadbeef@webrtc.org +orphis@webrtc.org diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc index a49b7ce48e..4234a655f4 100644 --- a/pc/audio_rtp_receiver.cc +++ b/pc/audio_rtp_receiver.cc @@ -12,6 +12,7 @@ #include +#include #include #include @@ -20,25 +21,29 @@ #include "pc/media_stream_track_proxy.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" -#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/task_utils/to_queued_task.h" namespace webrtc { -AudioRtpReceiver::AudioRtpReceiver(rtc::Thread* worker_thread, - std::string receiver_id, - std::vector stream_ids, - bool is_unified_plan) +AudioRtpReceiver::AudioRtpReceiver( + rtc::Thread* worker_thread, + std::string receiver_id, + std::vector stream_ids, + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel /*= nullptr*/) : AudioRtpReceiver(worker_thread, receiver_id, CreateStreamsFromIds(std::move(stream_ids)), - is_unified_plan) {} + is_unified_plan, + voice_channel) {} AudioRtpReceiver::AudioRtpReceiver( rtc::Thread* worker_thread, const std::string& receiver_id, const std::vector>& streams, - bool is_unified_plan) + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel /*= nullptr*/) : worker_thread_(worker_thread), id_(receiver_id), source_(rtc::make_ref_counted( @@ -49,7 +54,8 @@ AudioRtpReceiver::AudioRtpReceiver( track_(AudioTrackProxyWithInternal::Create( rtc::Thread::Current(), AudioTrack::Create(receiver_id, source_))), - cached_track_enabled_(track_->enabled()), + media_channel_(voice_channel), + cached_track_enabled_(track_->internal()->enabled()), attachment_id_(GenerateUniqueId()), worker_thread_safety_(PendingTaskSafetyFlag::CreateDetachedInactive()) { RTC_DCHECK(worker_thread_); @@ -69,15 +75,15 @@ AudioRtpReceiver::~AudioRtpReceiver() { void AudioRtpReceiver::OnChanged() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - if (cached_track_enabled_ != track_->enabled()) { - cached_track_enabled_ = track_->enabled(); - worker_thread_->PostTask(ToQueuedTask( - worker_thread_safety_, - [this, enabled = cached_track_enabled_, volume = cached_volume_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - Reconfigure(enabled, volume); - })); - } + const bool enabled = track_->internal()->enabled(); + if (cached_track_enabled_ == enabled) + return; + cached_track_enabled_ = enabled; + worker_thread_->PostTask( + ToQueuedTask(worker_thread_safety_, [this, enabled]() { + RTC_DCHECK_RUN_ON(worker_thread_); + Reconfigure(enabled); + })); } // RTC_RUN_ON(worker_thread_) @@ -97,20 +103,18 @@ void AudioRtpReceiver::OnSetVolume(double volume) { RTC_DCHECK_GE(volume, 0); RTC_DCHECK_LE(volume, 10); - // Update the cached_volume_ even when stopped, to allow clients to set the - // volume before starting/restarting, eg see crbug.com/1272566. - cached_volume_ = volume; - - // When the track is disabled, the volume of the source, which is the - // corresponding WebRtc Voice Engine channel will be 0. So we do not allow - // setting the volume to the source when the track is disabled. - if (track_->enabled()) { - worker_thread_->PostTask( - ToQueuedTask(worker_thread_safety_, [this, volume = cached_volume_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - SetOutputVolume_w(volume); - })); - } + bool track_enabled = track_->internal()->enabled(); + worker_thread_->Invoke(RTC_FROM_HERE, [&]() { + RTC_DCHECK_RUN_ON(worker_thread_); + // Update the cached_volume_ even when stopped, to allow clients to set + // the volume before starting/restarting, eg see crbug.com/1272566. + cached_volume_ = volume; + // When the track is disabled, the volume of the source, which is the + // corresponding WebRtc Voice Engine channel will be 0. So we do not + // allow setting the volume to the source when the track is disabled. + if (track_enabled) + SetOutputVolume_w(volume); + }); } rtc::scoped_refptr AudioRtpReceiver::dtls_transport() @@ -159,50 +163,47 @@ AudioRtpReceiver::GetFrameDecryptor() const { void AudioRtpReceiver::Stop() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - // TODO(deadbeef): Need to do more here to fully stop receiving packets. source_->SetState(MediaSourceInterface::kEnded); + track_->internal()->set_ended(); +} +// RTC_RUN_ON(&signaling_thread_checker_) +void AudioRtpReceiver::RestartMediaChannel(absl::optional ssrc) { + bool enabled = track_->internal()->enabled(); + MediaSourceInterface::SourceState state = source_->state(); worker_thread_->Invoke(RTC_FROM_HERE, [&]() { RTC_DCHECK_RUN_ON(worker_thread_); - - if (media_channel_) - SetOutputVolume_w(0.0); - - SetMediaChannel_w(nullptr); + RestartMediaChannel_w(std::move(ssrc), enabled, state); }); + source_->SetState(MediaSourceInterface::kLive); } -void AudioRtpReceiver::StopAndEndTrack() { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - Stop(); - track_->internal()->set_ended(); -} +// RTC_RUN_ON(worker_thread_) +void AudioRtpReceiver::RestartMediaChannel_w( + absl::optional ssrc, + bool track_enabled, + MediaSourceInterface::SourceState state) { + if (!media_channel_) + return; // Can't restart. -void AudioRtpReceiver::RestartMediaChannel(absl::optional ssrc) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - MediaSourceInterface::SourceState state = source_->state(); - worker_thread_->Invoke( - RTC_FROM_HERE, - [&, enabled = cached_track_enabled_, volume = cached_volume_]() { - RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) - return; // Can't restart. - - if (state != MediaSourceInterface::kInitializing) { - if (ssrc_ == ssrc) - return; - source_->Stop(media_channel_, ssrc_); - } - - ssrc_ = std::move(ssrc); - source_->Start(media_channel_, ssrc_); - if (ssrc_) { - media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); - } - - Reconfigure(enabled, volume); - }); - source_->SetState(MediaSourceInterface::kLive); + // Make sure the safety flag is marked as `alive` for cases where the media + // channel was provided via the ctor and not an explicit call to + // SetMediaChannel. + worker_thread_safety_->SetAlive(); + + if (state != MediaSourceInterface::kInitializing) { + if (ssrc_ == ssrc) + return; + source_->Stop(media_channel_, ssrc_); + } + + ssrc_ = std::move(ssrc); + source_->Start(media_channel_, ssrc_); + if (ssrc_) { + media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); + } + + Reconfigure(track_enabled); } void AudioRtpReceiver::SetupMediaChannel(uint32_t ssrc) { @@ -245,7 +246,7 @@ void AudioRtpReceiver::SetStreams( } } if (removed) { - existing_stream->RemoveTrack(track_); + existing_stream->RemoveTrack(audio_track()); } } // Add remote track to any streams that are new. @@ -259,7 +260,7 @@ void AudioRtpReceiver::SetStreams( } } if (added) { - stream->AddTrack(track_); + stream->AddTrack(audio_track()); } } streams_ = streams; @@ -284,10 +285,10 @@ void AudioRtpReceiver::SetDepacketizerToDecoderFrameTransformer( } // RTC_RUN_ON(worker_thread_) -void AudioRtpReceiver::Reconfigure(bool track_enabled, double volume) { +void AudioRtpReceiver::Reconfigure(bool track_enabled) { RTC_DCHECK(media_channel_); - SetOutputVolume_w(track_enabled ? volume : 0); + SetOutputVolume_w(track_enabled ? cached_volume_ : 0); if (ssrc_ && frame_decryptor_) { // Reattach the frame decryptor if we were reconfigured. @@ -318,18 +319,12 @@ void AudioRtpReceiver::SetJitterBufferMinimumDelay( } void AudioRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK_RUN_ON(worker_thread_); RTC_DCHECK(media_channel == nullptr || media_channel->media_type() == media_type()); + if (!media_channel && media_channel_) + SetOutputVolume_w(0.0); - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - SetMediaChannel_w(media_channel); - }); -} - -// RTC_RUN_ON(worker_thread_) -void AudioRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { media_channel ? worker_thread_safety_->SetAlive() : worker_thread_safety_->SetNotAlive(); media_channel_ = static_cast(media_channel); diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h index 978c550dfe..281ba69708 100644 --- a/pc/audio_rtp_receiver.h +++ b/pc/audio_rtp_receiver.h @@ -45,16 +45,24 @@ class AudioRtpReceiver : public ObserverInterface, public AudioSourceInterface::AudioObserver, public RtpReceiverInternal { public: + // The constructor supports optionally passing the voice channel to the + // instance at construction time without having to call `SetMediaChannel()` + // on the worker thread straight after construction. + // However, when using that, the assumption is that right after construction, + // a call to either `SetupUnsignaledMediaChannel` or `SetupMediaChannel` + // will be made, which will internally start the source on the worker thread. AudioRtpReceiver(rtc::Thread* worker_thread, std::string receiver_id, std::vector stream_ids, - bool is_unified_plan); + bool is_unified_plan, + cricket::VoiceMediaChannel* voice_channel = nullptr); // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed. AudioRtpReceiver( rtc::Thread* worker_thread, const std::string& receiver_id, const std::vector>& streams, - bool is_unified_plan); + bool is_unified_plan, + cricket::VoiceMediaChannel* media_channel = nullptr); virtual ~AudioRtpReceiver(); // ObserverInterface implementation @@ -90,7 +98,6 @@ class AudioRtpReceiver : public ObserverInterface, // RtpReceiverInternal implementation. void Stop() override; - void StopAndEndTrack() override; void SetupMediaChannel(uint32_t ssrc) override; void SetupUnsignaledMediaChannel() override; uint32_t ssrc() const override; @@ -114,12 +121,14 @@ class AudioRtpReceiver : public ObserverInterface, override; private: - void RestartMediaChannel(absl::optional ssrc); - void Reconfigure(bool track_enabled, double volume) + void RestartMediaChannel(absl::optional ssrc) + RTC_RUN_ON(&signaling_thread_checker_); + void RestartMediaChannel_w(absl::optional ssrc, + bool track_enabled, + MediaSourceInterface::SourceState state) RTC_RUN_ON(worker_thread_); + void Reconfigure(bool track_enabled) RTC_RUN_ON(worker_thread_); void SetOutputVolume_w(double volume) RTC_RUN_ON(worker_thread_); - void SetMediaChannel_w(cricket::MediaChannel* media_channel) - RTC_RUN_ON(worker_thread_); RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; rtc::Thread* const worker_thread_; @@ -132,7 +141,7 @@ class AudioRtpReceiver : public ObserverInterface, std::vector> streams_ RTC_GUARDED_BY(&signaling_thread_checker_); bool cached_track_enabled_ RTC_GUARDED_BY(&signaling_thread_checker_); - double cached_volume_ RTC_GUARDED_BY(&signaling_thread_checker_) = 1.0; + double cached_volume_ RTC_GUARDED_BY(worker_thread_) = 1.0; RtpReceiverObserverInterface* observer_ RTC_GUARDED_BY(&signaling_thread_checker_) = nullptr; bool received_first_packet_ RTC_GUARDED_BY(&signaling_thread_checker_) = diff --git a/pc/audio_rtp_receiver_unittest.cc b/pc/audio_rtp_receiver_unittest.cc index 763677b046..de72d3f9fb 100644 --- a/pc/audio_rtp_receiver_unittest.cc +++ b/pc/audio_rtp_receiver_unittest.cc @@ -10,12 +10,15 @@ #include "pc/audio_rtp_receiver.h" -#include "media/base/media_channel.h" +#include + #include "pc/test/mock_voice_media_channel.h" #include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/run_loop.h" using ::testing::_; using ::testing::InvokeWithoutArgs; @@ -24,6 +27,7 @@ using ::testing::Mock; static const int kTimeOut = 100; static const double kDefaultVolume = 1; static const double kVolume = 3.7; +static const double kVolumeMuted = 0.0; static const uint32_t kSsrc = 3; namespace webrtc { @@ -42,8 +46,8 @@ class AudioRtpReceiverTest : public ::testing::Test { } ~AudioRtpReceiverTest() { + EXPECT_CALL(media_channel_, SetOutputVolume(kSsrc, kVolumeMuted)); receiver_->SetMediaChannel(nullptr); - receiver_->Stop(); } rtc::Thread* worker_; @@ -90,4 +94,34 @@ TEST_F(AudioRtpReceiverTest, VolumesSetBeforeStartingAreRespected) { receiver_->SetupMediaChannel(kSsrc); } + +// Tests that OnChanged notifications are processed correctly on the worker +// thread when a media channel pointer is passed to the receiver via the +// constructor. +TEST(AudioRtpReceiver, OnChangedNotificationsAfterConstruction) { + webrtc::test::RunLoop loop; + auto* thread = rtc::Thread::Current(); // Points to loop's thread. + cricket::MockVoiceMediaChannel media_channel(thread); + auto receiver = rtc::make_ref_counted( + thread, std::string(), std::vector(), true, &media_channel); + + EXPECT_CALL(media_channel, SetDefaultRawAudioSink(_)).Times(1); + EXPECT_CALL(media_channel, SetDefaultOutputVolume(kDefaultVolume)).Times(1); + receiver->SetupUnsignaledMediaChannel(); + loop.Flush(); + + // Mark the track as disabled. + receiver->track()->set_enabled(false); + + // When the track was marked as disabled, an async notification was queued + // for the worker thread. This notification should trigger the volume + // of the media channel to be set to kVolumeMuted. + // Flush the worker thread, but set the expectation first for the call. + EXPECT_CALL(media_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1); + loop.Flush(); + + EXPECT_CALL(media_channel, SetDefaultOutputVolume(kVolumeMuted)).Times(1); + receiver->SetMediaChannel(nullptr); +} + } // namespace webrtc diff --git a/pc/channel.cc b/pc/channel.cc index b322ae2e73..9b8e8f8c39 100644 --- a/pc/channel.cc +++ b/pc/channel.cc @@ -12,26 +12,26 @@ #include #include -#include -#include +#include +#include #include -#include "absl/algorithm/container.h" #include "absl/strings/string_view.h" #include "api/rtp_parameters.h" #include "api/sequence_checker.h" -#include "api/task_queue/queued_task.h" +#include "api/units/timestamp.h" #include "media/base/codec.h" #include "media/base/rid_description.h" #include "media/base/rtp_utils.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "p2p/base/dtls_transport_internal.h" #include "pc/rtp_media_utils.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/network_route.h" #include "rtc_base/strings/string_format.h" -#include "rtc_base/synchronization/mutex.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/trace_event.h" @@ -117,7 +117,7 @@ BaseChannel::BaseChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -818,7 +818,7 @@ VoiceChannel::VoiceChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -941,7 +941,7 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, UniqueRandomIdGenerator* ssrc_generator) @@ -969,12 +969,6 @@ void VideoChannel::UpdateMediaSendRecvState_w() { << ToString(); } -void VideoChannel::FillBitrateInfo(BandwidthEstimationInfo* bwe_info) { - RTC_DCHECK_RUN_ON(worker_thread()); - VideoMediaChannel* mc = media_channel(); - mc->FillBitrateInfo(bwe_info); -} - bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content, SdpType type, std::string& error_desc) { diff --git a/pc/channel.h b/pc/channel.h index 930ca9bdfd..8e121b2b32 100644 --- a/pc/channel.h +++ b/pc/channel.h @@ -11,50 +11,35 @@ #ifndef PC_CHANNEL_H_ #define PC_CHANNEL_H_ -#include #include -#include +#include #include -#include #include #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "api/call/audio_sink.h" #include "api/crypto/crypto_options.h" -#include "api/function_view.h" #include "api/jsep.h" #include "api/media_types.h" -#include "api/rtp_receiver_interface.h" +#include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" -#include "api/video/video_sink_interface.h" -#include "api/video/video_source_interface.h" #include "call/rtp_demuxer.h" #include "call/rtp_packet_sink_interface.h" #include "media/base/media_channel.h" -#include "media/base/media_engine.h" #include "media/base/stream_params.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "p2p/base/dtls_transport_internal.h" -#include "p2p/base/packet_transport_internal.h" #include "pc/channel_interface.h" -#include "pc/dtls_srtp_transport.h" -#include "pc/media_session.h" -#include "pc/rtp_transport.h" #include "pc/rtp_transport_internal.h" #include "pc/session_description.h" -#include "pc/srtp_filter.h" -#include "pc/srtp_transport.h" #include "rtc_base/async_packet_socket.h" -#include "rtc_base/async_udp_socket.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/location.h" -#include "rtc_base/network.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/network_route.h" #include "rtc_base/socket.h" @@ -62,17 +47,10 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" -#include "rtc_base/thread_message.h" #include "rtc_base/unique_id_generator.h" -namespace webrtc { -class AudioSinkInterface; -} // namespace webrtc - namespace cricket { -struct CryptoParams; - // BaseChannel contains logic common to voice and video, including enable, // marshaling calls to a worker and network threads, and connection and media // monitors. @@ -105,7 +83,7 @@ class BaseChannel : public ChannelInterface, rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); @@ -180,6 +158,14 @@ class BaseChannel : public ChannelInterface, MediaChannel* media_channel() const override { return media_channel_.get(); } + VideoMediaChannel* video_media_channel() const override { + RTC_CHECK(false) << "Attempt to fetch video channel from non-video"; + return nullptr; + } + VoiceMediaChannel* voice_media_channel() const override { + RTC_CHECK(false) << "Attempt to fetch voice channel from non-voice"; + return nullptr; + } protected: void set_local_content_direction(webrtc::RtpTransceiverDirection direction) @@ -375,7 +361,7 @@ class VoiceChannel : public BaseChannel { rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); @@ -386,6 +372,10 @@ class VoiceChannel : public BaseChannel { return static_cast(BaseChannel::media_channel()); } + VoiceMediaChannel* voice_media_channel() const override { + return static_cast(media_channel()); + } + cricket::MediaType media_type() const override { return cricket::MEDIA_TYPE_AUDIO; } @@ -417,7 +407,7 @@ class VideoChannel : public BaseChannel { rtc::Thread* network_thread, rtc::Thread* signaling_thread, std::unique_ptr media_channel, - const std::string& mid, + absl::string_view mid, bool srtp_required, webrtc::CryptoOptions crypto_options, rtc::UniqueRandomIdGenerator* ssrc_generator); @@ -428,12 +418,14 @@ class VideoChannel : public BaseChannel { return static_cast(BaseChannel::media_channel()); } + VideoMediaChannel* video_media_channel() const override { + return static_cast(media_channel()); + } + cricket::MediaType media_type() const override { return cricket::MEDIA_TYPE_VIDEO; } - void FillBitrateInfo(BandwidthEstimationInfo* bwe_info); - private: // overrides from BaseChannel void UpdateMediaSendRecvState_w() RTC_RUN_ON(worker_thread()) override; diff --git a/pc/channel_factory_interface.h b/pc/channel_factory_interface.h new file mode 100644 index 0000000000..6cae0baad9 --- /dev/null +++ b/pc/channel_factory_interface.h @@ -0,0 +1,55 @@ +/* + * 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 PC_CHANNEL_FACTORY_INTERFACE_H_ +#define PC_CHANNEL_FACTORY_INTERFACE_H_ + +#include +#include + +#include "api/audio_options.h" +#include "api/crypto/crypto_options.h" +#include "api/video/video_bitrate_allocator_factory.h" +#include "call/call.h" +#include "media/base/media_channel.h" +#include "media/base/media_config.h" + +namespace cricket { + +class VideoChannel; +class VoiceChannel; + +class ChannelFactoryInterface { + public: + virtual std::unique_ptr CreateVideoChannel( + webrtc::Call* call, + const MediaConfig& media_config, + absl::string_view mid, + bool srtp_required, + const webrtc::CryptoOptions& crypto_options, + const VideoOptions& options, + webrtc::VideoBitrateAllocatorFactory* + video_bitrate_allocator_factory) = 0; + + virtual std::unique_ptr CreateVoiceChannel( + webrtc::Call* call, + const MediaConfig& media_config, + absl::string_view mid, + bool srtp_required, + const webrtc::CryptoOptions& crypto_options, + const AudioOptions& options) = 0; + + protected: + virtual ~ChannelFactoryInterface() = default; +}; + +} // namespace cricket + +#endif // PC_CHANNEL_FACTORY_INTERFACE_H_ diff --git a/pc/channel_interface.h b/pc/channel_interface.h index a16a9b753d..3c6ca6fe6a 100644 --- a/pc/channel_interface.h +++ b/pc/channel_interface.h @@ -11,6 +11,7 @@ #ifndef PC_CHANNEL_INTERFACE_H_ #define PC_CHANNEL_INTERFACE_H_ +#include #include #include @@ -28,8 +29,6 @@ class VideoBitrateAllocatorFactory; namespace cricket { class MediaContentDescription; -class VideoChannel; -class VoiceChannel; struct MediaConfig; // A Channel is a construct that groups media streams of the same type @@ -42,11 +41,17 @@ struct MediaConfig; // ChannelInterface contains methods common to voice and video channels. // As more methods are added to BaseChannel, they should be included in the // interface as well. +// TODO(bugs.webrtc.org/13931): Merge this class into RtpTransceiver. class ChannelInterface { public: + virtual ~ChannelInterface() = default; virtual cricket::MediaType media_type() const = 0; virtual MediaChannel* media_channel() const = 0; + // Typecasts of media_channel(). Will cause an exception if the + // channel is of the wrong type. + virtual VideoMediaChannel* video_media_channel() const = 0; + virtual VoiceMediaChannel* voice_media_channel() const = 0; // Returns a string view for the transport name. Fetching the transport name // must be done on the network thread only and note that the lifetime of @@ -83,35 +88,6 @@ class ChannelInterface { // * An SrtpTransport for SDES. // * A DtlsSrtpTransport for DTLS-SRTP. virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0; - - protected: - virtual ~ChannelInterface() = default; -}; - -class ChannelFactoryInterface { - public: - virtual VideoChannel* CreateVideoChannel( - webrtc::Call* call, - const MediaConfig& media_config, - const std::string& mid, - bool srtp_required, - const webrtc::CryptoOptions& crypto_options, - const VideoOptions& options, - webrtc::VideoBitrateAllocatorFactory* - video_bitrate_allocator_factory) = 0; - - virtual VoiceChannel* CreateVoiceChannel( - webrtc::Call* call, - const MediaConfig& media_config, - const std::string& mid, - bool srtp_required, - const webrtc::CryptoOptions& crypto_options, - const AudioOptions& options) = 0; - - virtual void DestroyChannel(ChannelInterface* channel) = 0; - - protected: - virtual ~ChannelFactoryInterface() = default; }; } // namespace cricket diff --git a/pc/channel_manager.cc b/pc/channel_manager.cc index 2146ed5087..e13db54806 100644 --- a/pc/channel_manager.cc +++ b/pc/channel_manager.cc @@ -10,17 +10,17 @@ #include "pc/channel_manager.h" -#include #include #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "api/media_types.h" #include "api/sequence_checker.h" #include "media/base/media_constants.h" +#include "pc/channel.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" -#include "rtc_base/logging.h" #include "rtc_base/trace_event.h" namespace cricket { @@ -63,8 +63,6 @@ ChannelManager::~ChannelManager() { RTC_DCHECK_RUN_ON(signaling_thread_); worker_thread_->Invoke(RTC_FROM_HERE, [&] { RTC_DCHECK_RUN_ON(worker_thread_); - RTC_DCHECK(voice_channels_.empty()); - RTC_DCHECK(video_channels_.empty()); // While `media_engine_` is const throughout the ChannelManager's lifetime, // it requires destruction to happen on the worker thread. Instead of // marking the pointer as non-const, we live with this const_cast<> in the @@ -151,10 +149,10 @@ ChannelManager::GetSupportedVideoRtpHeaderExtensions() const { return media_engine_->video().GetRtpHeaderExtensions(); } -VoiceChannel* ChannelManager::CreateVoiceChannel( +std::unique_ptr ChannelManager::CreateVoiceChannel( webrtc::Call* call, const MediaConfig& media_config, - const std::string& mid, + absl::string_view mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, const AudioOptions& options) { @@ -164,10 +162,11 @@ VoiceChannel* ChannelManager::CreateVoiceChannel( // PeerConnection and add the expectation that we're already on the right // thread. if (!worker_thread_->IsCurrent()) { - return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return CreateVoiceChannel(call, media_config, mid, srtp_required, - crypto_options, options); - }); + return worker_thread_->Invoke>( + RTC_FROM_HERE, [&] { + return CreateVoiceChannel(call, media_config, mid, srtp_required, + crypto_options, options); + }); } RTC_DCHECK_RUN_ON(worker_thread_); @@ -183,22 +182,13 @@ VoiceChannel* ChannelManager::CreateVoiceChannel( absl::WrapUnique(media_channel), mid, srtp_required, crypto_options, &ssrc_generator_); - VoiceChannel* voice_channel_ptr = voice_channel.get(); - voice_channels_.push_back(std::move(voice_channel)); - return voice_channel_ptr; + return voice_channel; } -void ChannelManager::DestroyVoiceChannel(VoiceChannel* channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel"); - RTC_DCHECK_RUN_ON(worker_thread_); - voice_channels_.erase(absl::c_find_if( - voice_channels_, [&](const auto& p) { return p.get() == channel; })); -} - -VideoChannel* ChannelManager::CreateVideoChannel( +std::unique_ptr ChannelManager::CreateVideoChannel( webrtc::Call* call, const MediaConfig& media_config, - const std::string& mid, + absl::string_view mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, const VideoOptions& options, @@ -209,11 +199,12 @@ VideoChannel* ChannelManager::CreateVideoChannel( // PeerConnection and add the expectation that we're already on the right // thread. if (!worker_thread_->IsCurrent()) { - return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return CreateVideoChannel(call, media_config, mid, srtp_required, - crypto_options, options, - video_bitrate_allocator_factory); - }); + return worker_thread_->Invoke>( + RTC_FROM_HERE, [&] { + return CreateVideoChannel(call, media_config, mid, srtp_required, + crypto_options, options, + video_bitrate_allocator_factory); + }); } RTC_DCHECK_RUN_ON(worker_thread_); @@ -230,37 +221,7 @@ VideoChannel* ChannelManager::CreateVideoChannel( absl::WrapUnique(media_channel), mid, srtp_required, crypto_options, &ssrc_generator_); - VideoChannel* video_channel_ptr = video_channel.get(); - video_channels_.push_back(std::move(video_channel)); - return video_channel_ptr; -} - -void ChannelManager::DestroyVideoChannel(VideoChannel* channel) { - TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel"); - RTC_DCHECK_RUN_ON(worker_thread_); - - video_channels_.erase(absl::c_find_if( - video_channels_, [&](const auto& p) { return p.get() == channel; })); -} - -void ChannelManager::DestroyChannel(ChannelInterface* channel) { - RTC_DCHECK(channel); - - if (!worker_thread_->IsCurrent()) { - // TODO(tommi): Do this asynchronously when we have a way to make sure that - // the call to DestroyChannel runs before ~Call() runs, which today happens - // inside an Invoke from the signaling thread in PeerConnectin::Close(). - worker_thread_->Invoke(RTC_FROM_HERE, - [&] { DestroyChannel(channel); }); - return; - } - - if (channel->media_type() == MEDIA_TYPE_AUDIO) { - DestroyVoiceChannel(static_cast(channel)); - } else { - RTC_DCHECK_EQ(channel->media_type(), MEDIA_TYPE_VIDEO); - DestroyVideoChannel(static_cast(channel)); - } + return video_channel; } bool ChannelManager::StartAecDump(webrtc::FileWrapper file, diff --git a/pc/channel_manager.h b/pc/channel_manager.h index a1c4efd55b..8d1ec28001 100644 --- a/pc/channel_manager.h +++ b/pc/channel_manager.h @@ -26,11 +26,12 @@ #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" -#include "pc/channel.h" -#include "pc/rtp_transport_internal.h" +#include "pc/channel_factory_interface.h" +#include "pc/channel_interface.h" #include "pc/session_description.h" #include "rtc_base/system/file_wrapper.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/unique_id_generator.h" namespace cricket { @@ -76,32 +77,31 @@ class ChannelManager : public ChannelFactoryInterface { GetSupportedVideoRtpHeaderExtensions() const; // The operations below all occur on the worker thread. - // ChannelManager retains ownership of the created channels, so clients should - // call the appropriate Destroy*Channel method when done. + // The caller is responsible for ensuring that destruction happens + // on the worker thread. // Creates a voice channel, to be associated with the specified session. - VoiceChannel* CreateVoiceChannel(webrtc::Call* call, - const MediaConfig& media_config, - const std::string& mid, - bool srtp_required, - const webrtc::CryptoOptions& crypto_options, - const AudioOptions& options) override; + std::unique_ptr CreateVoiceChannel( + webrtc::Call* call, + const MediaConfig& media_config, + absl::string_view mid, + bool srtp_required, + const webrtc::CryptoOptions& crypto_options, + const AudioOptions& options) override; // Creates a video channel, synced with the specified voice channel, and // associated with the specified session. // Version of the above that takes PacketTransportInternal. - VideoChannel* CreateVideoChannel( + std::unique_ptr CreateVideoChannel( webrtc::Call* call, const MediaConfig& media_config, - const std::string& mid, + absl::string_view mid, bool srtp_required, const webrtc::CryptoOptions& crypto_options, const VideoOptions& options, webrtc::VideoBitrateAllocatorFactory* video_bitrate_allocator_factory) override; - void DestroyChannel(ChannelInterface* channel) override; - // Starts AEC dump using existing file, with a specified maximum file size in // bytes. When the limit is reached, logging will stop and the file will be // closed. If max_size_bytes is set to <= 0, no limit will be used. @@ -134,12 +134,6 @@ class ChannelManager : public ChannelFactoryInterface { // and worker threads. See if we can't restrict usage to a single thread. rtc::UniqueRandomIdGenerator ssrc_generator_; - // Vector contents are non-null. - std::vector> voice_channels_ - RTC_GUARDED_BY(worker_thread_); - std::vector> video_channels_ - RTC_GUARDED_BY(worker_thread_); - const bool enable_rtx_; }; diff --git a/pc/channel_manager_unittest.cc b/pc/channel_manager_unittest.cc index 765e8e144d..6d7318b446 100644 --- a/pc/channel_manager_unittest.cc +++ b/pc/channel_manager_unittest.cc @@ -10,21 +10,22 @@ #include "pc/channel_manager.h" -#include - -#include "api/rtc_error.h" +#include "api/sequence_checker.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" #include "media/base/fake_media_engine.h" #include "media/base/test_utils.h" #include "media/engine/fake_webrtc_call.h" -#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/p2p_constants.h" -#include "p2p/base/packet_transport_internal.h" +#include "pc/channel.h" #include "pc/dtls_srtp_transport.h" +#include "pc/rtp_transport_internal.h" +#include "rtc_base/arraysize.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace cricket { namespace { @@ -68,19 +69,20 @@ class ChannelManagerTest : public ::testing::Test { void TestCreateDestroyChannels(webrtc::RtpTransportInternal* rtp_transport) { RTC_DCHECK_RUN_ON(worker_); - cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel( - &fake_call_, cricket::MediaConfig(), cricket::CN_AUDIO, - kDefaultSrtpRequired, webrtc::CryptoOptions(), AudioOptions()); + std::unique_ptr voice_channel = + cm_->CreateVoiceChannel(&fake_call_, cricket::MediaConfig(), + cricket::CN_AUDIO, kDefaultSrtpRequired, + webrtc::CryptoOptions(), AudioOptions()); ASSERT_TRUE(voice_channel != nullptr); - cricket::VideoChannel* video_channel = cm_->CreateVideoChannel( - &fake_call_, cricket::MediaConfig(), cricket::CN_VIDEO, - kDefaultSrtpRequired, webrtc::CryptoOptions(), VideoOptions(), - video_bitrate_allocator_factory_.get()); + std::unique_ptr video_channel = + cm_->CreateVideoChannel(&fake_call_, cricket::MediaConfig(), + cricket::CN_VIDEO, kDefaultSrtpRequired, + webrtc::CryptoOptions(), VideoOptions(), + video_bitrate_allocator_factory_.get()); ASSERT_TRUE(video_channel != nullptr); - - cm_->DestroyChannel(video_channel); - cm_->DestroyChannel(voice_channel); + // Destruction is tested by having the owning pointers + // go out of scope. } std::unique_ptr network_; @@ -89,6 +91,7 @@ class ChannelManagerTest : public ::testing::Test { video_bitrate_allocator_factory_; std::unique_ptr cm_; cricket::FakeCall fake_call_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; TEST_F(ChannelManagerTest, SetVideoRtxEnabled) { @@ -98,25 +101,25 @@ TEST_F(ChannelManagerTest, SetVideoRtxEnabled) { // By default RTX is disabled. cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); // Enable and check. cm_ = cricket::ChannelManager::Create(CreateFakeMediaEngine(), true, worker_, network_.get()); cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_TRUE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_TRUE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_TRUE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_TRUE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); // Disable and check. cm_ = cricket::ChannelManager::Create(CreateFakeMediaEngine(), false, worker_, network_.get()); cm_->GetSupportedVideoSendCodecs(&send_codecs); - EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(send_codecs, rtx_codec, &field_trials_)); cm_->GetSupportedVideoSendCodecs(&recv_codecs); - EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec)); + EXPECT_FALSE(ContainsMatchingCodec(recv_codecs, rtx_codec, &field_trials_)); } TEST_F(ChannelManagerTest, CreateDestroyChannels) { @@ -124,7 +127,8 @@ TEST_F(ChannelManagerTest, CreateDestroyChannels) { "fake_dtls_transport", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_.get()); auto dtls_srtp_transport = std::make_unique( - /*rtcp_mux_required=*/true); + /*rtcp_mux_required=*/true, field_trials_); + network_->Invoke( RTC_FROM_HERE, [&rtp_dtls_transport, &dtls_srtp_transport] { dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport.get(), diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc index 304c3b28b4..725ee34918 100644 --- a/pc/channel_unittest.cc +++ b/pc/channel_unittest.cc @@ -10,9 +10,11 @@ #include "pc/channel.h" +#include + #include -#include -#include +#include +#include #include "api/array_view.h" #include "api/audio_options.h" @@ -21,11 +23,15 @@ #include "media/base/fake_media_engine.h" #include "media/base/fake_rtp.h" #include "media/base/media_channel.h" +#include "media/base/media_constants.h" +#include "media/base/rid_description.h" #include "p2p/base/candidate_pair_interface.h" +#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_packet_transport.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" #include "pc/dtls_srtp_transport.h" #include "pc/jsep_transport.h" #include "pc/rtp_transport.h" @@ -33,12 +39,14 @@ #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::DtlsTransportInternal; using cricket::FakeVoiceMediaChannel; @@ -310,7 +318,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, field_trials_); network_thread_->Invoke( RTC_FROM_HERE, @@ -1434,6 +1442,7 @@ class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { rtc::Buffer rtcp_packet_; cricket::CandidatePairInterface* last_selected_candidate_pair_; rtc::UniqueRandomIdGenerator ssrc_generator_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; template <> @@ -2011,9 +2020,11 @@ TEST_F(VideoChannelSingleThreadTest, TestSetLocalOfferWithPacketization) { EXPECT_TRUE(channel1_->SetLocalContent(&video, SdpType::kOffer, err)); EXPECT_THAT(media_channel1()->send_codecs(), testing::IsEmpty()); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->recv_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->recv_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[1].packetization, cricket::kPacketizationParamRaw); } @@ -2032,9 +2043,11 @@ TEST_F(VideoChannelSingleThreadTest, TestSetRemoteOfferWithPacketization) { EXPECT_TRUE(err.empty()); EXPECT_THAT(media_channel1()->recv_codecs(), testing::IsEmpty()); ASSERT_THAT(media_channel1()->send_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->send_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->send_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[1].packetization, cricket::kPacketizationParamRaw); } @@ -2054,15 +2067,19 @@ TEST_F(VideoChannelSingleThreadTest, TestSetAnswerWithPacketization) { EXPECT_TRUE(channel1_->SetRemoteContent(&video, SdpType::kAnswer, err)); EXPECT_TRUE(err.empty()); ASSERT_THAT(media_channel1()->recv_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->recv_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->recv_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->recv_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->recv_codecs()[1].packetization, cricket::kPacketizationParamRaw); EXPECT_THAT(media_channel1()->send_codecs(), testing::SizeIs(2)); - EXPECT_TRUE(media_channel1()->send_codecs()[0].Matches(kVp8Codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[0].Matches(kVp8Codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[0].packetization, absl::nullopt); - EXPECT_TRUE(media_channel1()->send_codecs()[1].Matches(vp9_codec)); + EXPECT_TRUE( + media_channel1()->send_codecs()[1].Matches(vp9_codec, &field_trials_)); EXPECT_EQ(media_channel1()->send_codecs()[1].packetization, cricket::kPacketizationParamRaw); } diff --git a/pc/connection_context.cc b/pc/connection_context.cc index 6e531339f4..818283d636 100644 --- a/pc/connection_context.cc +++ b/pc/connection_context.cc @@ -10,14 +10,16 @@ #include "pc/connection_context.h" -#include #include #include #include "api/transport/field_trial_based_config.h" +#include "media/base/media_engine.h" #include "media/sctp/sctp_transport_factory.h" +#include "pc/channel_manager.h" #include "rtc_base/helpers.h" #include "rtc_base/internal/default_socket_server.h" +#include "rtc_base/socket_server.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/time_utils.h" @@ -42,18 +44,6 @@ rtc::Thread* MaybeStartNetworkThread( return thread_holder.get(); } -rtc::Thread* MaybeStartWorkerThread( - rtc::Thread* old_thread, - std::unique_ptr& thread_holder) { - if (old_thread) { - return old_thread; - } - thread_holder = rtc::Thread::Create(); - thread_holder->SetName("pc_worker_thread", nullptr); - thread_holder->Start(); - return thread_holder.get(); -} - rtc::Thread* MaybeWrapThread(rtc::Thread* signaling_thread, bool& wraps_current_thread) { wraps_current_thread = false; @@ -72,7 +62,8 @@ rtc::Thread* MaybeWrapThread(rtc::Thread* signaling_thread, std::unique_ptr MaybeCreateSctpFactory( std::unique_ptr factory, - rtc::Thread* network_thread) { + rtc::Thread* network_thread, + const FieldTrialsView& field_trials) { if (factory) { return factory; } @@ -97,20 +88,25 @@ ConnectionContext::ConnectionContext( : network_thread_(MaybeStartNetworkThread(dependencies->network_thread, owned_socket_factory_, owned_network_thread_)), - worker_thread_(MaybeStartWorkerThread(dependencies->worker_thread, - owned_worker_thread_)), + worker_thread_(dependencies->worker_thread, + []() { + auto thread_holder = rtc::Thread::Create(); + thread_holder->SetName("pc_worker_thread", nullptr); + thread_holder->Start(); + return thread_holder; + }), signaling_thread_(MaybeWrapThread(dependencies->signaling_thread, wraps_current_thread_)), + trials_(dependencies->trials ? std::move(dependencies->trials) + : std::make_unique()), network_monitor_factory_( std::move(dependencies->network_monitor_factory)), call_factory_(std::move(dependencies->call_factory)), sctp_factory_( MaybeCreateSctpFactory(std::move(dependencies->sctp_factory), - network_thread())), - trials_(dependencies->trials - ? std::move(dependencies->trials) - : std::make_unique()) { - signaling_thread_->AllowInvokesToThread(worker_thread_); + network_thread(), + *trials_.get())) { + signaling_thread_->AllowInvokesToThread(worker_thread()); signaling_thread_->AllowInvokesToThread(network_thread_); worker_thread_->AllowInvokesToThread(network_thread_); if (network_thread_->IsCurrent()) { @@ -143,7 +139,7 @@ ConnectionContext::ConnectionContext( // If network_monitor_factory_ is non-null, it will be used to create a // network monitor while on the network thread. default_network_manager_ = std::make_unique( - network_monitor_factory_.get(), socket_factory); + network_monitor_factory_.get(), socket_factory, &field_trials()); default_socket_factory_ = std::make_unique(socket_factory); diff --git a/pc/connection_context.h b/pc/connection_context.h index 5e814079f9..43239ffae0 100644 --- a/pc/connection_context.h +++ b/pc/connection_context.h @@ -15,23 +15,27 @@ #include #include "api/call/call_factory_interface.h" +#include "api/field_trials_view.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/transport/sctp_transport_factory_interface.h" -#include "api/transport/webrtc_key_value_config.h" #include "media/base/media_engine.h" #include "p2p/base/basic_packet_socket_factory.h" -#include "pc/channel_manager.h" #include "rtc_base/checks.h" #include "rtc_base/network.h" #include "rtc_base/network_monitor_factory.h" #include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_factory.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" +namespace cricket { +class ChannelManager; +} + namespace rtc { class BasicNetworkManager; class BasicPacketSocketFactory; @@ -69,12 +73,16 @@ class ConnectionContext final rtc::Thread* signaling_thread() { return signaling_thread_; } const rtc::Thread* signaling_thread() const { return signaling_thread_; } - rtc::Thread* worker_thread() { return worker_thread_; } - const rtc::Thread* worker_thread() const { return worker_thread_; } + rtc::Thread* worker_thread() { return worker_thread_.get(); } + const rtc::Thread* worker_thread() const { return worker_thread_.get(); } rtc::Thread* network_thread() { return network_thread_; } const rtc::Thread* network_thread() const { return network_thread_; } - const WebRtcKeyValueConfig& trials() const { return *trials_.get(); } + // Field trials associated with the PeerConnectionFactory. + // Note: that there can be different field trials for different + // PeerConnections (but they are not supposed change after creating the + // PeerConnection). + const FieldTrialsView& field_trials() const { return *trials_.get(); } // Accessors only used from the PeerConnectionFactory class rtc::BasicNetworkManager* default_network_manager() { @@ -86,7 +94,7 @@ class ConnectionContext final return default_socket_factory_.get(); } CallFactoryInterface* call_factory() { - RTC_DCHECK_RUN_ON(worker_thread_); + RTC_DCHECK_RUN_ON(worker_thread()); return call_factory_.get(); } @@ -100,31 +108,29 @@ class ConnectionContext final // The following three variables are used to communicate between the // constructor and the destructor, and are never exposed externally. bool wraps_current_thread_; - // Note: Since owned_network_thread_ and owned_worker_thread_ are used - // in the initialization of network_thread_ and worker_thread_, they - // must be declared before them, so that they are initialized first. std::unique_ptr owned_socket_factory_; std::unique_ptr owned_network_thread_ RTC_GUARDED_BY(signaling_thread_); - std::unique_ptr owned_worker_thread_ - RTC_GUARDED_BY(signaling_thread_); rtc::Thread* const network_thread_; - rtc::Thread* const worker_thread_; + AlwaysValidPointer const worker_thread_; rtc::Thread* const signaling_thread_; + + // Accessed both on signaling thread and worker thread. + std::unique_ptr const trials_; + // channel_manager is accessed both on signaling thread and worker thread. + // Const after construction, explicitly cleared in destructor. std::unique_ptr channel_manager_; std::unique_ptr const network_monitor_factory_ RTC_GUARDED_BY(signaling_thread_); std::unique_ptr default_network_manager_ RTC_GUARDED_BY(signaling_thread_); std::unique_ptr const call_factory_ - RTC_GUARDED_BY(worker_thread_); + RTC_GUARDED_BY(worker_thread()); std::unique_ptr default_socket_factory_ RTC_GUARDED_BY(signaling_thread_); std::unique_ptr const sctp_factory_; - // Accessed both on signaling thread and worker thread. - std::unique_ptr const trials_; }; } // namespace webrtc diff --git a/pc/data_channel_controller.cc b/pc/data_channel_controller.cc index adbf303105..8dde41724c 100644 --- a/pc/data_channel_controller.cc +++ b/pc/data_channel_controller.cc @@ -10,11 +10,8 @@ #include "pc/data_channel_controller.h" -#include #include -#include "absl/algorithm/container.h" -#include "absl/types/optional.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" #include "pc/peer_connection_internal.h" @@ -25,6 +22,15 @@ namespace webrtc { +DataChannelController::~DataChannelController() { + // Since channels may have multiple owners, we cannot guarantee that + // they will be deallocated before destroying the controller. + // Therefore, detach them from the controller. + for (auto channel : sctp_data_channels_) { + channel->DetachFromController(); + } +} + bool DataChannelController::HasDataChannels() const { RTC_DCHECK_RUN_ON(signaling_thread()); return !sctp_data_channels_.empty(); @@ -362,16 +368,6 @@ void DataChannelController::OnTransportChannelClosed(RTCError error) { } } -SctpDataChannel* DataChannelController::FindDataChannelBySid(int sid) const { - RTC_DCHECK_RUN_ON(signaling_thread()); - for (const auto& channel : sctp_data_channels_) { - if (channel->id() == sid) { - return channel; - } - } - return nullptr; -} - DataChannelTransportInterface* DataChannelController::data_channel_transport() const { // TODO(bugs.webrtc.org/11547): Only allow this accessor to be called on the diff --git a/pc/data_channel_controller.h b/pc/data_channel_controller.h index 00d38f0c84..cec79038c6 100644 --- a/pc/data_channel_controller.h +++ b/pc/data_channel_controller.h @@ -11,21 +11,15 @@ #ifndef PC_DATA_CHANNEL_CONTROLLER_H_ #define PC_DATA_CHANNEL_CONTROLLER_H_ -#include - -#include -#include #include #include #include "api/data_channel_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "api/transport/data_channel_transport_interface.h" #include "media/base/media_channel.h" -#include "media/base/media_engine.h" -#include "media/base/stream_params.h" -#include "pc/channel.h" #include "pc/data_channel_utils.h" #include "pc/sctp_data_channel.h" #include "rtc_base/checks.h" @@ -40,10 +34,11 @@ namespace webrtc { class PeerConnectionInternal; -class DataChannelController : public SctpDataChannelProviderInterface, +class DataChannelController : public SctpDataChannelControllerInterface, public DataChannelSink { public: explicit DataChannelController(PeerConnectionInternal* pc) : pc_(pc) {} + ~DataChannelController(); // Not copyable or movable. DataChannelController(DataChannelController&) = delete; @@ -93,8 +88,6 @@ class DataChannelController : public SctpDataChannelProviderInterface, config) /* RTC_RUN_ON(signaling_thread()) */; void AllocateSctpSids(rtc::SSLRole role); - SctpDataChannel* FindDataChannelBySid(int sid) const; - // Checks if any data channel has been added. bool HasDataChannels() const; bool HasSctpDataChannels() const { diff --git a/pc/data_channel_controller_unittest.cc b/pc/data_channel_controller_unittest.cc new file mode 100644 index 0000000000..7a1f68a52f --- /dev/null +++ b/pc/data_channel_controller_unittest.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2022 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 "pc/data_channel_controller.h" + +#include + +#include "pc/peer_connection_internal.h" +#include "pc/sctp_data_channel.h" +#include "pc/test/mock_peer_connection_internal.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +using ::testing::NiceMock; +using ::testing::Return; + +class DataChannelControllerTest : public ::testing::Test { + protected: + DataChannelControllerTest() { + pc_ = rtc::make_ref_counted>(); + ON_CALL(*pc_, signaling_thread) + .WillByDefault(Return(rtc::Thread::Current())); + } + + rtc::scoped_refptr> pc_; +}; + +TEST_F(DataChannelControllerTest, CreateAndDestroy) { + DataChannelController dcc(pc_.get()); +} + +TEST_F(DataChannelControllerTest, CreateDataChannelEarlyRelease) { + DataChannelController dcc(pc_.get()); + auto channel = dcc.InternalCreateDataChannelWithProxy( + "label", + std::make_unique(DataChannelInit()).get()); + channel = nullptr; // dcc holds a reference to channel, so not destroyed yet +} + +TEST_F(DataChannelControllerTest, CreateDataChannelLateRelease) { + auto dcc = std::make_unique(pc_.get()); + auto channel = dcc->InternalCreateDataChannelWithProxy( + "label", + std::make_unique(DataChannelInit()).get()); + dcc.reset(); + channel = nullptr; +} + +TEST_F(DataChannelControllerTest, CloseAfterControllerDestroyed) { + auto dcc = std::make_unique(pc_.get()); + auto channel = dcc->InternalCreateDataChannelWithProxy( + "label", + std::make_unique(DataChannelInit()).get()); + // Connect to provider + auto inner_channel = + DowncastProxiedDataChannelInterfaceToSctpDataChannelForTesting( + channel.get()); + dcc->ConnectDataChannel(inner_channel); + dcc.reset(); + channel->Close(); +} + +} // namespace +} // namespace webrtc diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc index c0dbfdd4bf..d184a81732 100644 --- a/pc/data_channel_integrationtest.cc +++ b/pc/data_channel_integrationtest.cc @@ -10,24 +10,36 @@ #include -#include -#include +#include +#include #include +#include #include +#include "absl/algorithm/container.h" #include "absl/types/optional.h" #include "api/data_channel_interface.h" -#include "api/dtmf_sender_interface.h" +#include "api/dtls_transport_interface.h" #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" +#include "api/stats/rtc_stats_report.h" +#include "api/stats/rtcstats_objects.h" #include "api/units/time_delta.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_session.h" +#include "pc/session_description.h" #include "pc/test/integration_test_helpers.h" #include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/fake_clock.h" #include "rtc_base/gunit.h" -#include "rtc_base/ref_counted_object.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_conversions.h" #include "rtc_base/virtual_socket_server.h" -#include "system_wrappers/include/field_trial.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { @@ -44,13 +56,12 @@ namespace { #define DISABLED_ON_ANDROID(t) t #endif -class DataChannelIntegrationTest : public PeerConnectionIntegrationBaseTest, - public ::testing::WithParamInterface< - std::tuple> { +class DataChannelIntegrationTest + : public PeerConnectionIntegrationBaseTest, + public ::testing::WithParamInterface { protected: DataChannelIntegrationTest() - : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam()), - std::get<1>(GetParam())) {} + : PeerConnectionIntegrationBaseTest(GetParam()) {} }; // Fake clock must be set before threads are started to prevent race on @@ -76,7 +87,7 @@ class DataChannelIntegrationTestPlanB : public PeerConnectionIntegrationBaseTest { protected: DataChannelIntegrationTestPlanB() - : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; class DataChannelIntegrationTestUnifiedPlan @@ -191,7 +202,7 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannel) { // data channel only, and sends messages of various sizes. TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelVariousSizes) { - ASSERT_TRUE(CreatePeerConnectionWrappers()); + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -230,7 +241,7 @@ TEST_P(DataChannelIntegrationTest, // data channel only, and sends empty messages TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelEmptyMessages) { - ASSERT_TRUE(CreatePeerConnectionWrappers()); + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -280,7 +291,7 @@ TEST_P(DataChannelIntegrationTest, // this test does not use TURN. const size_t kLowestSafePayloadSizeLimit = 1225; - ASSERT_TRUE(CreatePeerConnectionWrappers()); + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -317,7 +328,7 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelHarmfulMtu) { // The size of the smallest message that fails to be delivered. const size_t kMessageSizeThatIsNotDelivered = 1157; - ASSERT_TRUE(CreatePeerConnectionWrappers()); + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); ConnectFakeSignaling(); caller()->CreateDataChannel(); caller()->CreateAndSetAndSignalOffer(); @@ -408,7 +419,7 @@ TEST_P(DataChannelIntegrationTest, SctpDataChannelConfigSentToOtherSide) { EXPECT_FALSE(callee()->data_channel()->negotiated()); } -// Test usrsctp's ability to process unordered data stream, where data actually +// Test sctp's ability to process unordered data stream, where data actually // arrives out of order using simulated delays. Previously there have been some // bugs in this area. TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) { @@ -418,7 +429,7 @@ TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) { virtual_socket_server()->set_delay_stddev(5); virtual_socket_server()->UpdateDelayDistribution(); // Normal procedure, but with unordered data channel config. - ASSERT_TRUE(CreatePeerConnectionWrappers()); + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); ConnectFakeSignaling(); webrtc::DataChannelInit init; init.ordered = false; @@ -471,6 +482,154 @@ TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) { EXPECT_EQ(sent_messages, callee_received_messages); } +// Repeatedly open and close data channels on a peer connection to check that +// the channels are properly negotiated and SCTP stream IDs properly recycled. +TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelNoDelay) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + int channel_id = 0; + const size_t kChannelCount = 8; + const size_t kIterations = 10; + bool has_negotiated = false; + + webrtc::DataChannelInit init; + for (size_t repeats = 0; repeats < kIterations; ++repeats) { + RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations; + + for (size_t i = 0; i < kChannelCount; ++i) { + rtc::StringBuilder sb; + sb << "channel-" << channel_id++; + caller()->CreateDataChannel(sb.Release(), &init); + } + ASSERT_EQ(caller()->data_channels().size(), kChannelCount); + + if (!has_negotiated) { + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + has_negotiated = true; + } + + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(), + DataChannelInterface::DataState::kOpen, kDefaultTimeout); + RTC_LOG(LS_INFO) << "Caller Channel " + << caller()->data_channels()[i]->label() << " with id " + << caller()->data_channels()[i]->id() << " is open."; + } + ASSERT_EQ_WAIT(callee()->data_channels().size(), kChannelCount, + kDefaultTimeout); + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(), + DataChannelInterface::DataState::kOpen, kDefaultTimeout); + RTC_LOG(LS_INFO) << "Callee Channel " + << callee()->data_channels()[i]->label() << " with id " + << callee()->data_channels()[i]->id() << " is open."; + } + + // Closing from both sides to attempt creating races. + // A real application would likely only close from one side. + for (size_t i = 0; i < kChannelCount; ++i) { + if (i % 3 == 0) { + callee()->data_channels()[i]->Close(); + caller()->data_channels()[i]->Close(); + } else { + caller()->data_channels()[i]->Close(); + callee()->data_channels()[i]->Close(); + } + } + + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(), + DataChannelInterface::DataState::kClosed, kDefaultTimeout); + ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(), + DataChannelInterface::DataState::kClosed, kDefaultTimeout); + } + + caller()->data_channels().clear(); + caller()->data_observers().clear(); + callee()->data_channels().clear(); + callee()->data_observers().clear(); + } +} + +// Repeatedly open and close data channels on a peer connection to check that +// the channels are properly negotiated and SCTP stream IDs properly recycled. +// Some delay is added for better coverage. +TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelWithDelay) { + // Simulate some network delay + virtual_socket_server()->set_delay_mean(20); + virtual_socket_server()->set_delay_stddev(5); + virtual_socket_server()->UpdateDelayDistribution(); + + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + + int channel_id = 0; + const size_t kChannelCount = 8; + const size_t kIterations = 10; + bool has_negotiated = false; + + webrtc::DataChannelInit init; + for (size_t repeats = 0; repeats < kIterations; ++repeats) { + RTC_LOG(LS_INFO) << "Iteration " << (repeats + 1) << "/" << kIterations; + + for (size_t i = 0; i < kChannelCount; ++i) { + rtc::StringBuilder sb; + sb << "channel-" << channel_id++; + caller()->CreateDataChannel(sb.Release(), &init); + } + ASSERT_EQ(caller()->data_channels().size(), kChannelCount); + + if (!has_negotiated) { + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + has_negotiated = true; + } + + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(), + DataChannelInterface::DataState::kOpen, kDefaultTimeout); + RTC_LOG(LS_INFO) << "Caller Channel " + << caller()->data_channels()[i]->label() << " with id " + << caller()->data_channels()[i]->id() << " is open."; + } + ASSERT_EQ_WAIT(callee()->data_channels().size(), kChannelCount, + kDefaultTimeout); + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(), + DataChannelInterface::DataState::kOpen, kDefaultTimeout); + RTC_LOG(LS_INFO) << "Callee Channel " + << callee()->data_channels()[i]->label() << " with id " + << callee()->data_channels()[i]->id() << " is open."; + } + + // Closing from both sides to attempt creating races. + // A real application would likely only close from one side. + for (size_t i = 0; i < kChannelCount; ++i) { + if (i % 3 == 0) { + callee()->data_channels()[i]->Close(); + caller()->data_channels()[i]->Close(); + } else { + caller()->data_channels()[i]->Close(); + callee()->data_channels()[i]->Close(); + } + } + + for (size_t i = 0; i < kChannelCount; ++i) { + ASSERT_EQ_WAIT(caller()->data_channels()[i]->state(), + DataChannelInterface::DataState::kClosed, kDefaultTimeout); + ASSERT_EQ_WAIT(callee()->data_channels()[i]->state(), + DataChannelInterface::DataState::kClosed, kDefaultTimeout); + } + + caller()->data_channels().clear(); + caller()->data_observers().clear(); + callee()->data_channels().clear(); + callee()->data_observers().clear(); + } +} + // This test sets up a call between two parties with audio, and video. When // audio and video are setup and flowing, an SCTP data channel is negotiated. TEST_P(DataChannelIntegrationTest, AddSctpDataChannelInSubsequentOffer) { @@ -501,10 +660,10 @@ TEST_P(DataChannelIntegrationTest, AddSctpDataChannelInSubsequentOffer) { kDefaultTimeout); } -// Set up a connection initially just using SCTP data channels, later upgrading -// to audio/video, ensuring frames are received end-to-end. Effectively the -// inverse of the test above. -// This was broken in M57; see https://crbug.com/711243 +// Set up a connection initially just using SCTP data channels, later +// upgrading to audio/video, ensuring frames are received end-to-end. +// Effectively the inverse of the test above. This was broken in M57; see +// https://crbug.com/711243 TEST_P(DataChannelIntegrationTest, SctpDataChannelToAudioVideoUpgrade) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -562,8 +721,8 @@ TEST_P(DataChannelIntegrationTest, kDefaultTimeout); } -// Test that after closing PeerConnections, they stop sending any packets (ICE, -// DTLS, RTP...). +// Test that after closing PeerConnections, they stop sending any packets +// (ICE, DTLS, RTP...). TEST_P(DataChannelIntegrationTest, ClosingConnectionStopsPacketFlow) { // Set up audio/video/data, wait for some frames to be received. ASSERT_TRUE(CreatePeerConnectionWrappers()); @@ -618,9 +777,8 @@ TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetNormally) { ->Information() .role(), DtlsTransportTlsRole::kClient); - // ID should be assigned according to the odd/even rule based on role; client - // gets even numbers, server gets odd ones. - // RFC 8832 section 6. + // ID should be assigned according to the odd/even rule based on role; + // client gets even numbers, server gets odd ones. RFC 8832 section 6. // TODO(hta): Test multiple channels. EXPECT_EQ(caller()->data_channel()->id(), 1); } @@ -656,9 +814,8 @@ TEST_P(DataChannelIntegrationTest, DtlsRoleIsSetWhenReversed) { ->Information() .role(), DtlsTransportTlsRole::kServer); - // ID should be assigned according to the odd/even rule based on role; client - // gets even numbers, server gets odd ones. - // RFC 8832 section 6. + // ID should be assigned according to the odd/even rule based on role; + // client gets even numbers, server gets odd ones. RFC 8832 section 6. // TODO(hta): Test multiple channels. EXPECT_EQ(caller()->data_channel()->id(), 0); } @@ -701,9 +858,8 @@ TEST_P(DataChannelIntegrationTest, ->Information() .role(), DtlsTransportTlsRole::kServer); - // ID should be assigned according to the odd/even rule based on role; client - // gets even numbers, server gets odd ones. - // RFC 8832 section 6. + // ID should be assigned according to the odd/even rule based on role; + // client gets even numbers, server gets odd ones. RFC 8832 section 6. ASSERT_EQ(caller()->data_channels().size(), 2U); ASSERT_EQ(callee()->data_channels().size(), 2U); EXPECT_EQ(caller()->data_channels()[0]->id(), 0); @@ -832,17 +988,8 @@ TEST_P(DataChannelIntegrationTest, EXPECT_GT(202u, callee()->data_observer()->received_message_count()); EXPECT_LE(2u, callee()->data_observer()->received_message_count()); // Then, check that observed behavior (lose some messages) has not changed - if (!webrtc::field_trial::IsDisabled("WebRTC-DataChannel-Dcsctp")) { - // DcSctp loses all messages. This is correct. - EXPECT_EQ(2u, callee()->data_observer()->received_message_count()); - } else { - // Usrsctp loses some messages, but keeps messages not attempted. - // THIS IS THE WRONG BEHAVIOR. According to discussion in - // https://github.com/sctplab/usrsctp/issues/584, all these packets - // should be discarded. - // TODO(bugs.webrtc.org/12731): Fix this. - EXPECT_EQ(90u, callee()->data_observer()->received_message_count()); - } + // DcSctp loses all messages. This is correct. + EXPECT_EQ(2u, callee()->data_observer()->received_message_count()); } TEST_P(DataChannelIntegrationTest, @@ -906,12 +1053,10 @@ TEST_P(DataChannelIntegrationTest, callee()->data_observer()->received_message_count()); } -INSTANTIATE_TEST_SUITE_P( - DataChannelIntegrationTest, - DataChannelIntegrationTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), - Values("WebRTC-DataChannel-Dcsctp/Enabled/", - "WebRTC-DataChannel-Dcsctp/Disabled/"))); +INSTANTIATE_TEST_SUITE_P(DataChannelIntegrationTest, + DataChannelIntegrationTest, + Values(SdpSemantics::kPlanB_DEPRECATED, + SdpSemantics::kUnifiedPlan)); TEST_F(DataChannelIntegrationTestUnifiedPlan, EndToEndCallWithBundledSctpDataChannel) { diff --git a/pc/data_channel_unittest.cc b/pc/data_channel_unittest.cc index 44c080bbda..55f9c34fea 100644 --- a/pc/data_channel_unittest.cc +++ b/pc/data_channel_unittest.cc @@ -8,17 +8,27 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include +#include #include +#include "api/data_channel_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "api/transport/data_channel_transport_interface.h" +#include "media/base/media_channel.h" #include "media/sctp/sctp_transport_internal.h" #include "pc/sctp_data_channel.h" #include "pc/sctp_utils.h" -#include "pc/test/fake_data_channel_provider.h" +#include "pc/test/fake_data_channel_controller.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" -#include "rtc_base/numerics/safe_conversions.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" #include "test/gtest.h" using webrtc::DataChannelInterface; @@ -62,27 +72,27 @@ class FakeDataChannelObserver : public webrtc::DataChannelObserver { size_t on_buffered_amount_change_count_; }; -// TODO(deadbeef): The fact that these tests use a fake provider makes them not -// too valuable. Should rewrite using the +// TODO(deadbeef): The fact that these tests use a fake controller makes them +// not too valuable. Should rewrite using the // peerconnection_datachannel_unittest.cc infrastructure. // TODO(bugs.webrtc.org/11547): Incorporate a dedicated network thread. class SctpDataChannelTest : public ::testing::Test { protected: SctpDataChannelTest() - : provider_(new FakeDataChannelProvider()), - webrtc_data_channel_(SctpDataChannel::Create(provider_.get(), + : controller_(new FakeDataChannelController()), + webrtc_data_channel_(SctpDataChannel::Create(controller_.get(), "test", init_, rtc::Thread::Current(), rtc::Thread::Current())) {} void SetChannelReady() { - provider_->set_transport_available(true); + controller_->set_transport_available(true); webrtc_data_channel_->OnTransportChannelCreated(); if (webrtc_data_channel_->id() < 0) { webrtc_data_channel_->SetSctpSid(0); } - provider_->set_ready_to_send(true); + controller_->set_ready_to_send(true); } void AddObserver() { @@ -91,7 +101,7 @@ class SctpDataChannelTest : public ::testing::Test { } webrtc::InternalDataChannelInit init_; - std::unique_ptr provider_; + std::unique_ptr controller_; std::unique_ptr observer_; rtc::scoped_refptr webrtc_data_channel_; }; @@ -112,29 +122,29 @@ class StateSignalsListener : public sigslot::has_slots<> { // Verifies that the data channel is connected to the transport after creation. TEST_F(SctpDataChannelTest, ConnectedToTransportOnCreated) { - provider_->set_transport_available(true); + controller_->set_transport_available(true); rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", init_, + SctpDataChannel::Create(controller_.get(), "test1", init_, rtc::Thread::Current(), rtc::Thread::Current()); - EXPECT_TRUE(provider_->IsConnected(dc.get())); + EXPECT_TRUE(controller_->IsConnected(dc.get())); // The sid is not set yet, so it should not have added the streams. - EXPECT_FALSE(provider_->IsSendStreamAdded(dc->id())); - EXPECT_FALSE(provider_->IsRecvStreamAdded(dc->id())); + EXPECT_FALSE(controller_->IsSendStreamAdded(dc->id())); + EXPECT_FALSE(controller_->IsRecvStreamAdded(dc->id())); dc->SetSctpSid(0); - EXPECT_TRUE(provider_->IsSendStreamAdded(dc->id())); - EXPECT_TRUE(provider_->IsRecvStreamAdded(dc->id())); + EXPECT_TRUE(controller_->IsSendStreamAdded(dc->id())); + EXPECT_TRUE(controller_->IsRecvStreamAdded(dc->id())); } // Verifies that the data channel is connected to the transport if the transport // is not available initially and becomes available later. TEST_F(SctpDataChannelTest, ConnectedAfterTransportBecomesAvailable) { - EXPECT_FALSE(provider_->IsConnected(webrtc_data_channel_.get())); + EXPECT_FALSE(controller_->IsConnected(webrtc_data_channel_.get())); - provider_->set_transport_available(true); + controller_->set_transport_available(true); webrtc_data_channel_->OnTransportChannelCreated(); - EXPECT_TRUE(provider_->IsConnected(webrtc_data_channel_.get())); + EXPECT_TRUE(controller_->IsConnected(webrtc_data_channel_.get())); } // Tests the state of the data channel. @@ -160,7 +170,7 @@ TEST_F(SctpDataChannelTest, StateTransition) { EXPECT_EQ(state_signals_listener.opened_count(), 1); EXPECT_EQ(state_signals_listener.closed_count(), 1); // Verifies that it's disconnected from the transport. - EXPECT_FALSE(provider_->IsConnected(webrtc_data_channel_.get())); + EXPECT_FALSE(controller_->IsConnected(webrtc_data_channel_.get())); } // Tests that DataChannel::buffered_amount() is correct after the channel is @@ -176,7 +186,7 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) { EXPECT_EQ(successful_send_count, observer_->on_buffered_amount_change_count()); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); const int number_of_packets = 3; for (int i = 0; i < number_of_packets; ++i) { @@ -187,7 +197,7 @@ TEST_F(SctpDataChannelTest, BufferedAmountWhenBlocked) { EXPECT_EQ(successful_send_count, observer_->on_buffered_amount_change_count()); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); successful_send_count += number_of_packets; EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount()); EXPECT_EQ(successful_send_count, @@ -200,12 +210,12 @@ TEST_F(SctpDataChannelTest, QueuedDataSentWhenUnblocked) { AddObserver(); SetChannelReady(); webrtc::DataBuffer buffer("abcd"); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); EXPECT_EQ(0U, observer_->on_buffered_amount_change_count()); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); SetChannelReady(); EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount()); EXPECT_EQ(1U, observer_->on_buffered_amount_change_count()); @@ -217,7 +227,7 @@ TEST_F(SctpDataChannelTest, BlockedWhenSendQueuedDataNoCrash) { AddObserver(); SetChannelReady(); webrtc::DataBuffer buffer("abcd"); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); EXPECT_EQ(0U, observer_->on_buffered_amount_change_count()); @@ -227,7 +237,7 @@ TEST_F(SctpDataChannelTest, BlockedWhenSendQueuedDataNoCrash) { EXPECT_EQ(0U, observer_->on_buffered_amount_change_count()); // Unblock the channel to send queued data again, there should be no crash. - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); SetChannelReady(); EXPECT_EQ(0U, webrtc_data_channel_->buffered_amount()); EXPECT_EQ(1U, observer_->on_buffered_amount_change_count()); @@ -252,7 +262,7 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesSent) { EXPECT_EQ(0U, webrtc_data_channel_->bytes_sent()); // Send three buffers while not blocked. - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[0])); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[1])); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[2])); @@ -262,7 +272,7 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesSent) { EXPECT_EQ(bytes_sent, webrtc_data_channel_->bytes_sent()); // Send three buffers while blocked, queuing the buffers. - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[3])); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[4])); EXPECT_TRUE(webrtc_data_channel_->Send(buffers[5])); @@ -273,7 +283,7 @@ TEST_F(SctpDataChannelTest, VerifyMessagesAndBytesSent) { EXPECT_EQ(bytes_sent, webrtc_data_channel_->bytes_sent()); // Unblock and make sure everything was sent. - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_EQ_WAIT(0U, webrtc_data_channel_->buffered_amount(), kDefaultTimeout); bytes_sent += bytes_queued; EXPECT_EQ(6U, webrtc_data_channel_->messages_sent()); @@ -288,18 +298,18 @@ TEST_F(SctpDataChannelTest, OpenMessageSent) { SetChannelReady(); EXPECT_GE(webrtc_data_channel_->id(), 0); EXPECT_EQ(webrtc::DataMessageType::kControl, - provider_->last_send_data_params().type); - EXPECT_EQ(provider_->last_sid(), webrtc_data_channel_->id()); + controller_->last_send_data_params().type); + EXPECT_EQ(controller_->last_sid(), webrtc_data_channel_->id()); } TEST_F(SctpDataChannelTest, QueuedOpenMessageSent) { - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); SetChannelReady(); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_EQ(webrtc::DataMessageType::kControl, - provider_->last_send_data_params().type); - EXPECT_EQ(provider_->last_sid(), webrtc_data_channel_->id()); + controller_->last_send_data_params().type); + EXPECT_EQ(controller_->last_sid(), webrtc_data_channel_->id()); } // Tests that the DataChannel created after transport gets ready can enter OPEN @@ -309,7 +319,7 @@ TEST_F(SctpDataChannelTest, LateCreatedChannelTransitionToOpen) { webrtc::InternalDataChannelInit init; init.id = 1; rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", init, + SctpDataChannel::Create(controller_.get(), "test1", init, rtc::Thread::Current(), rtc::Thread::Current()); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, dc->state()); EXPECT_TRUE_WAIT(webrtc::DataChannelInterface::kOpen == dc->state(), 1000); @@ -323,7 +333,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) { init.id = 1; init.ordered = false; rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", init, + SctpDataChannel::Create(controller_.get(), "test1", init, rtc::Thread::Current(), rtc::Thread::Current()); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); @@ -331,7 +341,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) { // Sends a message and verifies it's ordered. webrtc::DataBuffer buffer("some data"); ASSERT_TRUE(dc->Send(buffer)); - EXPECT_TRUE(provider_->last_send_data_params().ordered); + EXPECT_TRUE(controller_->last_send_data_params().ordered); // Emulates receiving an OPEN_ACK message. cricket::ReceiveDataParams params; @@ -343,7 +353,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceivesOpenAck) { // Sends another message and verifies it's unordered. ASSERT_TRUE(dc->Send(buffer)); - EXPECT_FALSE(provider_->last_send_data_params().ordered); + EXPECT_FALSE(controller_->last_send_data_params().ordered); } // Tests that an unordered DataChannel sends unordered data after any DATA @@ -354,7 +364,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) { init.id = 1; init.ordered = false; rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", init, + SctpDataChannel::Create(controller_.get(), "test1", init, rtc::Thread::Current(), rtc::Thread::Current()); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); @@ -368,7 +378,7 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) { // Sends a message and verifies it's unordered. ASSERT_TRUE(dc->Send(buffer)); - EXPECT_FALSE(provider_->last_send_data_params().ordered); + EXPECT_FALSE(controller_->last_send_data_params().ordered); } // Tests that the channel can't open until it's successfully sent the OPEN @@ -376,37 +386,37 @@ TEST_F(SctpDataChannelTest, SendUnorderedAfterReceiveData) { TEST_F(SctpDataChannelTest, OpenWaitsForOpenMesssage) { webrtc::DataBuffer buffer("foo"); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); SetChannelReady(); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, webrtc_data_channel_->state()); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, webrtc_data_channel_->state(), 1000); EXPECT_EQ(webrtc::DataMessageType::kControl, - provider_->last_send_data_params().type); + controller_->last_send_data_params().type); } // Tests that close first makes sure all queued data gets sent. TEST_F(SctpDataChannelTest, QueuedCloseFlushes) { webrtc::DataBuffer buffer("foo"); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); SetChannelReady(); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, webrtc_data_channel_->state()); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, webrtc_data_channel_->state(), 1000); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); webrtc_data_channel_->Send(buffer); webrtc_data_channel_->Close(); - provider_->set_send_blocked(false); + controller_->set_send_blocked(false); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, webrtc_data_channel_->state(), 1000); EXPECT_TRUE(webrtc_data_channel_->error().ok()); EXPECT_EQ(webrtc::DataMessageType::kText, - provider_->last_send_data_params().type); + controller_->last_send_data_params().type); } // Tests that messages are sent with the right id. @@ -415,7 +425,7 @@ TEST_F(SctpDataChannelTest, SendDataId) { SetChannelReady(); webrtc::DataBuffer buffer("data"); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); - EXPECT_EQ(1, provider_->last_sid()); + EXPECT_EQ(1, controller_->last_sid()); } // Tests that the incoming messages with wrong ids are rejected. @@ -458,11 +468,11 @@ TEST_F(SctpDataChannelTest, NoMsgSentIfNegotiatedAndNotFromOpenMsg) { SetChannelReady(); rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", config, + SctpDataChannel::Create(controller_.get(), "test1", config, rtc::Thread::Current(), rtc::Thread::Current()); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); - EXPECT_EQ(0, provider_->last_sid()); + EXPECT_EQ(0, controller_->last_sid()); } // Tests that DataChannel::messages_received() and DataChannel::bytes_received() @@ -522,14 +532,14 @@ TEST_F(SctpDataChannelTest, OpenAckSentIfCreatedFromOpenMessage) { SetChannelReady(); rtc::scoped_refptr dc = - SctpDataChannel::Create(provider_.get(), "test1", config, + SctpDataChannel::Create(controller_.get(), "test1", config, rtc::Thread::Current(), rtc::Thread::Current()); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kOpen, dc->state(), 1000); - EXPECT_EQ(config.id, provider_->last_sid()); + EXPECT_EQ(config.id, controller_->last_sid()); EXPECT_EQ(webrtc::DataMessageType::kControl, - provider_->last_send_data_params().type); + controller_->last_send_data_params().type); } // Tests the OPEN_ACK role assigned by InternalDataChannelInit. @@ -555,7 +565,7 @@ TEST_F(SctpDataChannelTest, OpenWhenSendBufferFull) { memset(buffer.MutableData(), 0, buffer.size()); webrtc::DataBuffer packet(buffer, true); - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); for (size_t i = 0; i < webrtc::DataChannelInterface::MaxSendQueueSize() / packetSize; ++i) { @@ -573,7 +583,7 @@ TEST_F(SctpDataChannelTest, OpenWhenSendBufferFull) { TEST_F(SctpDataChannelTest, ClosedOnTransportError) { SetChannelReady(); webrtc::DataBuffer buffer("abcd"); - provider_->set_transport_error(); + controller_->set_transport_error(); EXPECT_TRUE(webrtc_data_channel_->Send(buffer)); @@ -621,7 +631,7 @@ TEST_F(SctpDataChannelTest, SendEmptyData) { // Tests that a channel can be closed without being opened or assigned an sid. TEST_F(SctpDataChannelTest, NeverOpened) { - provider_->set_transport_available(true); + controller_->set_transport_available(true); webrtc_data_channel_->OnTransportChannelCreated(); webrtc_data_channel_->Close(); } @@ -636,7 +646,7 @@ TEST_F(SctpDataChannelTest, TransportDestroyedWhileDataBuffered) { webrtc::DataBuffer packet(buffer, true); // Send a packet while sending is blocked so it ends up buffered. - provider_->set_send_blocked(true); + controller_->set_send_blocked(true); EXPECT_TRUE(webrtc_data_channel_->Send(packet)); // Tell the data channel that its transport is being destroyed. @@ -645,7 +655,7 @@ TEST_F(SctpDataChannelTest, TransportDestroyedWhileDataBuffered) { webrtc::RTCError error(webrtc::RTCErrorType::OPERATION_ERROR_WITH_DATA, ""); error.set_error_detail(webrtc::RTCErrorDetailType::SCTP_FAILURE); webrtc_data_channel_->OnTransportChannelClosed(error); - provider_.reset(nullptr); + controller_.reset(nullptr); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, webrtc_data_channel_->state(), kDefaultTimeout); EXPECT_FALSE(webrtc_data_channel_->error().ok()); @@ -667,7 +677,7 @@ TEST_F(SctpDataChannelTest, TransportGotErrorCode) { error.set_sctp_cause_code( static_cast(cricket::SctpErrorCauseCode::kProtocolViolation)); webrtc_data_channel_->OnTransportChannelClosed(error); - provider_.reset(nullptr); + controller_.reset(nullptr); EXPECT_EQ_WAIT(webrtc::DataChannelInterface::kClosed, webrtc_data_channel_->state(), kDefaultTimeout); EXPECT_FALSE(webrtc_data_channel_->error().ok()); diff --git a/pc/dtls_srtp_transport.cc b/pc/dtls_srtp_transport.cc index 9a4135bb2f..28de50b2ae 100644 --- a/pc/dtls_srtp_transport.cc +++ b/pc/dtls_srtp_transport.cc @@ -27,8 +27,9 @@ static const char kDtlsSrtpExporterLabel[] = "EXTRACTOR-dtls_srtp"; namespace webrtc { -DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled) - : SrtpTransport(rtcp_mux_enabled) {} +DtlsSrtpTransport::DtlsSrtpTransport(bool rtcp_mux_enabled, + const FieldTrialsView& field_trials) + : SrtpTransport(rtcp_mux_enabled, field_trials) {} void DtlsSrtpTransport::SetDtlsTransports( cricket::DtlsTransportInternal* rtp_dtls_transport, diff --git a/pc/dtls_srtp_transport.h b/pc/dtls_srtp_transport.h index da068c9b8a..7958210c99 100644 --- a/pc/dtls_srtp_transport.h +++ b/pc/dtls_srtp_transport.h @@ -11,6 +11,7 @@ #ifndef PC_DTLS_SRTP_TRANSPORT_H_ #define PC_DTLS_SRTP_TRANSPORT_H_ +#include #include #include @@ -31,7 +32,7 @@ namespace webrtc { // configures the SrtpSessions in the base class. class DtlsSrtpTransport : public SrtpTransport { public: - explicit DtlsSrtpTransport(bool rtcp_mux_enabled); + DtlsSrtpTransport(bool rtcp_mux_enabled, const FieldTrialsView& field_trials); // Set P2P layer RTP/RTCP DtlsTransports. When using RTCP-muxing, // `rtcp_dtls_transport` is null. diff --git a/pc/dtls_srtp_transport_unittest.cc b/pc/dtls_srtp_transport_unittest.cc index 72df81a923..30f09e740c 100644 --- a/pc/dtls_srtp_transport_unittest.cc +++ b/pc/dtls_srtp_transport_unittest.cc @@ -14,7 +14,6 @@ #include #include -#include #include "call/rtp_demuxer.h" #include "media/base/fake_rtp.h" @@ -26,10 +25,13 @@ #include "pc/test/rtp_transport_test_util.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_identity.h" +#include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::FakeDtlsTransport; using cricket::FakeIceTransport; @@ -58,7 +60,7 @@ class DtlsSrtpTransportTest : public ::testing::Test, FakeDtlsTransport* rtcp_dtls, bool rtcp_mux_enabled) { auto dtls_srtp_transport = - std::make_unique(rtcp_mux_enabled); + std::make_unique(rtcp_mux_enabled, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls, rtcp_dtls); @@ -255,6 +257,7 @@ class DtlsSrtpTransportTest : public ::testing::Test, webrtc::TransportObserver transport_observer2_; int sequence_number_ = 0; + webrtc::test::ScopedKeyValueConfig field_trials_; }; // Tests that if RTCP muxing is enabled and transports are set after RTP diff --git a/pc/dtls_transport.cc b/pc/dtls_transport.cc index e8d6ae9b6a..c9f3279fbc 100644 --- a/pc/dtls_transport.cc +++ b/pc/dtls_transport.cc @@ -19,7 +19,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" -#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_stream_adapter.h" namespace webrtc { diff --git a/pc/dtls_transport_unittest.cc b/pc/dtls_transport_unittest.cc index 1400ff94c2..a9ac73c9b8 100644 --- a/pc/dtls_transport_unittest.cc +++ b/pc/dtls_transport_unittest.cc @@ -13,9 +13,15 @@ #include #include -#include "absl/memory/memory.h" +#include "absl/types/optional.h" +#include "api/rtc_error.h" #include "p2p/base/fake_dtls_transport.h" +#include "p2p/base/p2p_constants.h" +#include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_identity.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/pc/dtmf_sender.cc b/pc/dtmf_sender.cc index 1148350aa2..8b82c31aa9 100644 --- a/pc/dtmf_sender.cc +++ b/pc/dtmf_sender.cc @@ -13,8 +13,6 @@ #include #include -#include - #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" diff --git a/pc/dtmf_sender.h b/pc/dtmf_sender.h index 915d9874b3..ae213b3bf4 100644 --- a/pc/dtmf_sender.h +++ b/pc/dtmf_sender.h @@ -17,12 +17,14 @@ #include "api/dtmf_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" #include "pc/proxy.h" #include "rtc_base/location.h" #include "rtc_base/ref_count.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" // DtmfSender is the native implementation of the RTCDTMFSender defined by // the WebRTC W3C Editor's Draft. @@ -102,6 +104,7 @@ class DtmfSender : public DtmfSenderInterface, public sigslot::has_slots<> { // Define proxy for DtmfSenderInterface. BEGIN_PRIMARY_PROXY_MAP(DtmfSender) + PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_METHOD1(void, RegisterObserver, DtmfSenderObserverInterface*) PROXY_METHOD0(void, UnregisterObserver) diff --git a/pc/external_hmac.h b/pc/external_hmac.h index 3319beaed4..c5071fc192 100644 --- a/pc/external_hmac.h +++ b/pc/external_hmac.h @@ -30,9 +30,9 @@ #include -#include "third_party/libsrtp/crypto/include/auth.h" #include "third_party/libsrtp/crypto/include/crypto_types.h" #include "third_party/libsrtp/include/srtp.h" +#include "third_party/libsrtp/include/srtp_priv.h" #define EXTERNAL_HMAC_SHA1 SRTP_HMAC_SHA1 + 1 #define HMAC_KEY_LENGTH 20 diff --git a/pc/g3doc/sctp_transport.md b/pc/g3doc/sctp_transport.md index 863d289484..266387cdf0 100644 --- a/pc/g3doc/sctp_transport.md +++ b/pc/g3doc/sctp_transport.md @@ -28,10 +28,9 @@ closes, but the object itself may survive longer than the PeerConnection. ## cricket::SctpTransportInternal -[`cricket::SctpTransportInternal`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/media/sctp/sctp_transport_internal.h?q=cricket::SctpTransportInternal) owns two objects: The SCTP association object (currently -implemented by wrapping the usrsctp library) and the DTLS transport, which is -the object used to send and receive messages as emitted from or consumed by the -usrsctp library. +[`cricket::SctpTransportInternal`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/media/sctp/sctp_transport_internal.h?q=cricket::SctpTransportInternal) owns two objects: The SCTP association object +and the DTLS transport, which is the object used to send and receive messages +as emitted from or consumed by the sctp library. It communicates state changes and events using sigslot. diff --git a/pc/ice_server_parsing.cc b/pc/ice_server_parsing.cc index 88f77bf0a9..cb4145be1a 100644 --- a/pc/ice_server_parsing.cc +++ b/pc/ice_server_parsing.cc @@ -12,9 +12,7 @@ #include -#include #include // For std::isdigit. -#include #include #include "p2p/base/port_interface.h" diff --git a/pc/ice_transport.h b/pc/ice_transport.h index 11f3de5d27..e31ec546b2 100644 --- a/pc/ice_transport.h +++ b/pc/ice_transport.h @@ -12,7 +12,6 @@ #define PC_ICE_TRANSPORT_H_ #include "api/ice_transport_interface.h" -#include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" diff --git a/pc/ice_transport_unittest.cc b/pc/ice_transport_unittest.cc index ebb46cb5d5..95af2cd552 100644 --- a/pc/ice_transport_unittest.cc +++ b/pc/ice_transport_unittest.cc @@ -12,13 +12,12 @@ #include #include -#include #include "api/ice_transport_factory.h" +#include "api/scoped_refptr.h" #include "p2p/base/fake_ice_transport.h" #include "p2p/base/fake_port_allocator.h" -#include "rtc_base/gunit.h" -#include "test/gmock.h" +#include "rtc_base/ref_counted_object.h" #include "test/gtest.h" namespace webrtc { diff --git a/pc/jitter_buffer_delay.h b/pc/jitter_buffer_delay.h index dc10e3d2ba..a6bec01ce7 100644 --- a/pc/jitter_buffer_delay.h +++ b/pc/jitter_buffer_delay.h @@ -16,6 +16,7 @@ #include "absl/types/optional.h" #include "api/sequence_checker.h" #include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { diff --git a/pc/jitter_buffer_delay_unittest.cc b/pc/jitter_buffer_delay_unittest.cc index b00075ceb5..79c39fffb8 100644 --- a/pc/jitter_buffer_delay_unittest.cc +++ b/pc/jitter_buffer_delay_unittest.cc @@ -10,9 +10,6 @@ #include "pc/jitter_buffer_delay.h" -#include - -#include "absl/types/optional.h" #include "test/gtest.h" namespace webrtc { diff --git a/pc/jsep_ice_candidate.cc b/pc/jsep_ice_candidate.cc index 6dacde629c..1e97ad42d8 100644 --- a/pc/jsep_ice_candidate.cc +++ b/pc/jsep_ice_candidate.cc @@ -10,8 +10,6 @@ #include "api/jsep_ice_candidate.h" -#include - #include "pc/webrtc_sdp.h" // This file contains JsepIceCandidate-related functions that are not diff --git a/pc/jsep_session_description.cc b/pc/jsep_session_description.cc index 57ccf7ca6e..4c57396f08 100644 --- a/pc/jsep_session_description.cc +++ b/pc/jsep_session_description.cc @@ -11,11 +11,20 @@ #include "api/jsep_session_description.h" #include +#include +#include "absl/types/optional.h" +#include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" -#include "pc/media_session.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_session.h" // IWYU pragma: keep #include "pc/webrtc_sdp.h" -#include "rtc_base/arraysize.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/socket_address.h" using cricket::SessionDescription; diff --git a/pc/jsep_session_description_unittest.cc b/pc/jsep_session_description_unittest.cc index 2202aa81d0..ee446cbbda 100644 --- a/pc/jsep_session_description_unittest.cc +++ b/pc/jsep_session_description_unittest.cc @@ -13,8 +13,6 @@ #include #include -#include -#include #include #include @@ -29,6 +27,7 @@ #include "pc/session_description.h" #include "pc/webrtc_sdp.h" #include "rtc_base/helpers.h" +#include "rtc_base/net_helper.h" #include "rtc_base/socket_address.h" #include "rtc_base/string_encode.h" #include "test/gtest.h" diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc index 706c342a8d..65dd703e6c 100644 --- a/pc/jsep_transport.cc +++ b/pc/jsep_transport.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include "api/array_view.h" @@ -201,7 +202,7 @@ webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription( if (!local_fp) { local_certificate_ = nullptr; } else { - error = VerifyCertificateFingerprint(local_certificate_, local_fp); + error = VerifyCertificateFingerprint(local_certificate_.get(), local_fp); if (!error.ok()) { local_description_.reset(); return error; @@ -713,6 +714,10 @@ bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport, dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite); dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite); substats.dtls_state = dtls_transport->dtls_state(); + rtc::SSLRole dtls_role; + if (dtls_transport->GetDtlsRole(&dtls_role)) { + substats.dtls_role = dtls_role; + } if (!dtls_transport->ice_transport()->GetStats( &substats.ice_transport_stats)) { return false; diff --git a/pc/jsep_transport_collection.cc b/pc/jsep_transport_collection.cc index df44a9c157..b50d303d77 100644 --- a/pc/jsep_transport_collection.cc +++ b/pc/jsep_transport_collection.cc @@ -219,6 +219,22 @@ const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( return it == mid_to_transport_.end() ? nullptr : it->second; } +cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + absl::string_view mid) { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // TODO(hta): should be a better way. + auto it = mid_to_transport_.find(std::string(mid)); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + +const cricket::JsepTransport* JsepTransportCollection::GetTransportForMid( + absl::string_view mid) const { + RTC_DCHECK_RUN_ON(&sequence_checker_); + // TODO(hta): Should be a better way + auto it = mid_to_transport_.find(std::string(mid)); + return it == mid_to_transport_.end() ? nullptr : it->second; +} + bool JsepTransportCollection::SetTransportForMid( const std::string& mid, cricket::JsepTransport* jsep_transport) { diff --git a/pc/jsep_transport_collection.h b/pc/jsep_transport_collection.h index aa5293475e..0bb19a3260 100644 --- a/pc/jsep_transport_collection.h +++ b/pc/jsep_transport_collection.h @@ -18,6 +18,7 @@ #include #include +#include "api/jsep.h" #include "api/peer_connection_interface.h" #include "api/sequence_checker.h" #include "pc/jsep_transport.h" @@ -117,6 +118,8 @@ class JsepTransportCollection { cricket::JsepTransport* GetTransportForMid(const std::string& mid); const cricket::JsepTransport* GetTransportForMid( const std::string& mid) const; + cricket::JsepTransport* GetTransportForMid(absl::string_view mid); + const cricket::JsepTransport* GetTransportForMid(absl::string_view mid) const; // Set transport for a MID. This may destroy a transport if it is no // longer in use. bool SetTransportForMid(const std::string& mid, diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index b7e9f361bc..7b445741a2 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -12,9 +12,9 @@ #include -#include #include #include +#include #include #include @@ -62,6 +62,7 @@ JsepTransportController::JsepTransportController( RTC_DCHECK(config_.rtcp_handler); RTC_DCHECK(config_.ice_transport_factory); RTC_DCHECK(config_.on_dtls_handshake_error_); + RTC_DCHECK(config_.field_trials); } JsepTransportController::~JsepTransportController() { @@ -106,7 +107,7 @@ RTCError JsepTransportController::SetRemoteDescription( } RtpTransportInternal* JsepTransportController::GetRtpTransport( - const std::string& mid) const { + absl::string_view mid) const { RTC_DCHECK_RUN_ON(network_thread_); auto jsep_transport = GetJsepTransportForMid(mid); if (!jsep_transport) { @@ -399,6 +400,7 @@ JsepTransportController::CreateIceTransport(const std::string& transport_name, init.set_port_allocator(port_allocator_); init.set_async_dns_resolver_factory(async_dns_resolver_factory_); init.set_event_log(config_.event_log); + init.set_field_trials(config_.field_trials); return config_.ice_transport_factory->CreateIceTransport( transport_name, component, std::move(init)); } @@ -477,8 +479,8 @@ JsepTransportController::CreateSdesTransport( cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { RTC_DCHECK_RUN_ON(network_thread_); - auto srtp_transport = - std::make_unique(rtcp_dtls_transport == nullptr); + auto srtp_transport = std::make_unique( + rtcp_dtls_transport == nullptr, *config_.field_trials); RTC_DCHECK(rtp_dtls_transport); srtp_transport->SetRtpPacketTransport(rtp_dtls_transport); if (rtcp_dtls_transport) { @@ -497,7 +499,7 @@ JsepTransportController::CreateDtlsSrtpTransport( cricket::DtlsTransportInternal* rtcp_dtls_transport) { RTC_DCHECK_RUN_ON(network_thread_); auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, *config_.field_trials); if (config_.enable_external_auth) { dtls_srtp_transport->EnableExternalAuth(); } @@ -998,6 +1000,15 @@ cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid( const std::string& mid) { return transports_.GetTransportForMid(mid); } +const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid( + absl::string_view mid) const { + return transports_.GetTransportForMid(mid); +} + +cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid( + absl::string_view mid) { + return transports_.GetTransportForMid(mid); +} const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName( const std::string& transport_name) const { diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index d207269d09..ccb7426510 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -45,7 +46,6 @@ #include "p2p/base/port_allocator.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" -#include "pc/channel.h" #include "pc/dtls_srtp_transport.h" #include "pc/dtls_transport.h" #include "pc/jsep_transport.h" @@ -136,6 +136,9 @@ class JsepTransportController : public sigslot::has_slots<> { // Factory for SCTP transports. SctpTransportFactoryInterface* sctp_factory = nullptr; std::function on_dtls_handshake_error_; + + // Field trials. + const webrtc::FieldTrialsView* field_trials; }; // The ICE related events are fired on the `network_thread`. @@ -164,7 +167,7 @@ class JsepTransportController : public sigslot::has_slots<> { // Get transports to be used for the provided `mid`. If bundling is enabled, // calling GetRtpTransport for multiple MIDs may yield the same object. - RtpTransportInternal* GetRtpTransport(const std::string& mid) const; + RtpTransportInternal* GetRtpTransport(absl::string_view mid) const; cricket::DtlsTransportInternal* GetDtlsTransport(const std::string& mid); const cricket::DtlsTransportInternal* GetRtcpDtlsTransport( const std::string& mid) const; @@ -359,6 +362,10 @@ class JsepTransportController : public sigslot::has_slots<> { const std::string& mid) const RTC_RUN_ON(network_thread_); cricket::JsepTransport* GetJsepTransportForMid(const std::string& mid) RTC_RUN_ON(network_thread_); + const cricket::JsepTransport* GetJsepTransportForMid( + absl::string_view mid) const RTC_RUN_ON(network_thread_); + cricket::JsepTransport* GetJsepTransportForMid(absl::string_view mid) + RTC_RUN_ON(network_thread_); // Get the JsepTransport without considering the BUNDLE group. Return nullptr // if the JsepTransport is destroyed. diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc index 11706bb949..01724b38e3 100644 --- a/pc/jsep_transport_controller_unittest.cc +++ b/pc/jsep_transport_controller_unittest.cc @@ -11,16 +11,29 @@ #include "pc/jsep_transport_controller.h" #include -#include +#include +#include #include "api/dtls_transport_interface.h" +#include "api/transport/enums.h" +#include "p2p/base/candidate_pair_interface.h" #include "p2p/base/dtls_transport_factory.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_ice_transport.h" +#include "p2p/base/p2p_constants.h" #include "p2p/base/transport_info.h" +#include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" +#include "rtc_base/location.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/ssl_identity.h" #include "rtc_base/thread.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using cricket::Candidate; using cricket::Candidates; @@ -88,6 +101,7 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, config.ice_transport_factory = fake_ice_transport_factory_.get(); config.dtls_transport_factory = fake_dtls_transport_factory_.get(); config.on_dtls_handshake_error_ = [](rtc::SSLHandshakeError s) {}; + config.field_trials = &field_trials_; transport_controller_ = std::make_unique( network_thread, port_allocator, nullptr /* async_resolver_factory */, config); @@ -366,6 +380,7 @@ class JsepTransportControllerTest : public JsepTransportController::Observer, // Transport controller needs to be destroyed first, because it may issue // callbacks that modify the changed_*_by_mid in the destructor. std::unique_ptr transport_controller_; + webrtc::test::ScopedKeyValueConfig field_trials_; }; TEST_F(JsepTransportControllerTest, GetRtpTransport) { diff --git a/pc/jsep_transport_unittest.cc b/pc/jsep_transport_unittest.cc index d06fa9596a..c63dc496db 100644 --- a/pc/jsep_transport_unittest.cc +++ b/pc/jsep_transport_unittest.cc @@ -10,15 +10,34 @@ #include "pc/jsep_transport.h" -#include +#include +#include + +#include +#include #include #include -#include "api/ice_transport_factory.h" +#include "api/candidate.h" #include "media/base/fake_rtp.h" #include "p2p/base/fake_dtls_transport.h" #include "p2p/base/fake_ice_transport.h" -#include "rtc_base/gunit.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/buffer.h" +#include "rtc_base/byte_order.h" +#include "rtc_base/copy_on_write_buffer.h" +#include "rtc_base/helpers.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace cricket { namespace { @@ -71,7 +90,7 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { rtc::PacketTransportInternal* rtp_packet_transport, rtc::PacketTransportInternal* rtcp_packet_transport) { auto srtp_transport = std::make_unique( - rtcp_packet_transport == nullptr); + rtcp_packet_transport == nullptr, field_trials_); srtp_transport->SetRtpPacketTransport(rtp_packet_transport); if (rtcp_packet_transport) { @@ -84,7 +103,7 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { cricket::DtlsTransportInternal* rtp_dtls_transport, cricket::DtlsTransportInternal* rtcp_dtls_transport) { auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); + rtcp_dtls_transport == nullptr, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport, rtcp_dtls_transport); return dtls_srtp_transport; @@ -174,6 +193,8 @@ class JsepTransport2Test : public ::testing::Test, public sigslot::has_slots<> { // The SrtpTransport is owned by `jsep_transport_`. Keep a raw pointer here // for testing. webrtc::SrtpTransport* sdes_transport_ = nullptr; + + webrtc::test::ScopedKeyValueConfig field_trials_; }; // The parameterized tests cover both cases when RTCP mux is enable and diff --git a/pc/media_session.cc b/pc/media_session.cc index 96c285e67e..71a6d0a19c 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -14,8 +14,7 @@ #include #include -#include -#include +#include #include #include @@ -24,7 +23,6 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/crypto_params.h" -#include "api/video_codecs/h264_profile_level_id.h" #include "media/base/codec.h" #include "media/base/media_constants.h" #include "media/base/sdp_video_format_utils.h" @@ -41,7 +39,6 @@ #include "rtc_base/string_encode.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/unique_id_generator.h" -#include "system_wrappers/include/field_trial.h" namespace { @@ -303,7 +300,8 @@ static StreamParams CreateStreamParamsForNewSenderWithSsrcs( const std::string& rtcp_cname, bool include_rtx_streams, bool include_flexfec_stream, - UniqueRandomIdGenerator* ssrc_generator) { + UniqueRandomIdGenerator* ssrc_generator, + const webrtc::FieldTrialsView& field_trials) { StreamParams result; result.id = sender.track_id; @@ -315,8 +313,7 @@ static StreamParams CreateStreamParamsForNewSenderWithSsrcs( "a single media streams. This session has multiple " "media streams however, so no FlexFEC SSRC will be generated."; } - if (include_flexfec_stream && - !webrtc::field_trial::IsEnabled("WebRTC-FlexFEC-03")) { + if (include_flexfec_stream && !field_trials.IsEnabled("WebRTC-FlexFEC-03")) { include_flexfec_stream = false; RTC_LOG(LS_WARNING) << "WebRTC-FlexFEC trial is not enabled, not sending FlexFEC"; @@ -398,12 +395,12 @@ static void AddSimulcastToMediaDescription( // content_description. // `current_params` - All currently known StreamParams of any media type. template -static bool AddStreamParams( - const std::vector& sender_options, - const std::string& rtcp_cname, - UniqueRandomIdGenerator* ssrc_generator, - StreamParamsVec* current_streams, - MediaContentDescriptionImpl* content_description) { +static bool AddStreamParams(const std::vector& sender_options, + const std::string& rtcp_cname, + UniqueRandomIdGenerator* ssrc_generator, + StreamParamsVec* current_streams, + MediaContentDescriptionImpl* content_description, + const webrtc::FieldTrialsView& field_trials) { // SCTP streams are not negotiated using SDP/ContentDescriptions. if (IsSctpProtocol(content_description->protocol())) { return true; @@ -425,7 +422,7 @@ static bool AddStreamParams( // Signal SSRCs and legacy simulcast (if requested). CreateStreamParamsForNewSenderWithSsrcs( sender, rtcp_cname, include_rtx_streams, - include_flexfec_stream, ssrc_generator) + include_flexfec_stream, ssrc_generator, field_trials) : // Signal RIDs and spec-compliant simulcast (if requested). CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname); @@ -713,11 +710,12 @@ static bool CreateMediaContentOffer( const RtpHeaderExtensions& rtp_extensions, UniqueRandomIdGenerator* ssrc_generator, StreamParamsVec* current_streams, - MediaContentDescriptionImpl* offer) { + MediaContentDescriptionImpl* offer, + const webrtc::FieldTrialsView& field_trials) { offer->AddCodecs(codecs); if (!AddStreamParams(media_description_options.sender_options, session_options.rtcp_cname, ssrc_generator, - current_streams, offer)) { + current_streams, offer, field_trials)) { return false; } @@ -731,10 +729,12 @@ template static bool ReferencedCodecsMatch(const std::vector& codecs1, const int codec1_id, const std::vector& codecs2, - const int codec2_id) { + const int codec2_id, + const webrtc::FieldTrialsView* field_trials) { const C* codec1 = FindCodecById(codecs1, codec1_id); const C* codec2 = FindCodecById(codecs2, codec2_id); - return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2); + return codec1 != nullptr && codec2 != nullptr && + codec1->Matches(*codec2, field_trials); } template @@ -754,12 +754,14 @@ template static void NegotiateCodecs(const std::vector& local_codecs, const std::vector& offered_codecs, std::vector* negotiated_codecs, - bool keep_offer_order) { + bool keep_offer_order, + const webrtc::FieldTrialsView* field_trials) { for (const C& ours : local_codecs) { C theirs; // Note that we intentionally only find one matching codec for each of our // local codecs, in case the remote offer contains duplicate codecs. - if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) { + if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs, + field_trials)) { C negotiated = ours; NegotiatePacketization(ours, theirs, &negotiated); negotiated.IntersectFeedbackParams(theirs); @@ -815,7 +817,8 @@ template static bool FindMatchingCodec(const std::vector& codecs1, const std::vector& codecs2, const C& codec_to_match, - C* found_codec) { + C* found_codec, + const webrtc::FieldTrialsView* field_trials) { // `codec_to_match` should be a member of `codecs1`, in order to look up // RED/RTX codecs' associated codecs correctly. If not, that's a programming // error. @@ -823,7 +826,7 @@ static bool FindMatchingCodec(const std::vector& codecs1, return &codec == &codec_to_match; })); for (const C& potential_match : codecs2) { - if (potential_match.Matches(codec_to_match)) { + if (potential_match.Matches(codec_to_match, field_trials)) { if (IsRtxCodec(codec_to_match)) { int apt_value_1 = 0; int apt_value_2 = 0; @@ -834,8 +837,8 @@ static bool FindMatchingCodec(const std::vector& codecs1, RTC_LOG(LS_WARNING) << "RTX missing associated payload type."; continue; } - if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, - apt_value_2)) { + if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2, apt_value_2, + field_trials)) { continue; } } else if (IsRedCodec(codec_to_match)) { @@ -878,7 +881,7 @@ static bool FindMatchingCodec(const std::vector& codecs1, if (rtc::FromString(redundant_payloads_1[0], &red_value_1) && rtc::FromString(redundant_payloads_2[0], &red_value_2)) { if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2, - red_value_2)) { + red_value_2, field_trials)) { continue; } } @@ -969,14 +972,15 @@ static const C* GetAssociatedCodecForRed(const std::vector& codec_list, template static void MergeCodecs(const std::vector& reference_codecs, std::vector* offered_codecs, - UsedPayloadTypes* used_pltypes) { + UsedPayloadTypes* used_pltypes, + const webrtc::FieldTrialsView* field_trials) { // Add all new codecs that are not RTX/RED codecs. // The two-pass splitting of the loops means preferring payload types // of actual codecs with respect to collisions. for (const C& reference_codec : reference_codecs) { if (!IsRtxCodec(reference_codec) && !IsRedCodec(reference_codec) && !FindMatchingCodec(reference_codecs, *offered_codecs, - reference_codec, nullptr)) { + reference_codec, nullptr, field_trials)) { C codec = reference_codec; used_pltypes->FindAndSetIdUsed(&codec); offered_codecs->push_back(codec); @@ -987,7 +991,7 @@ static void MergeCodecs(const std::vector& reference_codecs, for (const C& reference_codec : reference_codecs) { if (IsRtxCodec(reference_codec) && !FindMatchingCodec(reference_codecs, *offered_codecs, - reference_codec, nullptr)) { + reference_codec, nullptr, field_trials)) { C rtx_codec = reference_codec; const C* associated_codec = GetAssociatedCodecForRtx(reference_codecs, rtx_codec); @@ -998,7 +1002,8 @@ static void MergeCodecs(const std::vector& reference_codecs, // Its payload type may be different than the reference codec. C matching_codec; if (!FindMatchingCodec(reference_codecs, *offered_codecs, - *associated_codec, &matching_codec)) { + *associated_codec, &matching_codec, + field_trials)) { RTC_LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name << " codec."; continue; @@ -1010,14 +1015,15 @@ static void MergeCodecs(const std::vector& reference_codecs, offered_codecs->push_back(rtx_codec); } else if (IsRedCodec(reference_codec) && !FindMatchingCodec(reference_codecs, *offered_codecs, - reference_codec, nullptr)) { + reference_codec, nullptr, field_trials)) { C red_codec = reference_codec; const C* associated_codec = GetAssociatedCodecForRed(reference_codecs, red_codec); if (associated_codec) { C matching_codec; if (!FindMatchingCodec(reference_codecs, *offered_codecs, - *associated_codec, &matching_codec)) { + *associated_codec, &matching_codec, + field_trials)) { RTC_LOG(LS_WARNING) << "Couldn't find matching " << associated_codec->name << " codec."; continue; @@ -1041,7 +1047,8 @@ template static Codecs MatchCodecPreference( const std::vector& codec_preferences, const Codecs& codecs, - const Codecs& supported_codecs) { + const Codecs& supported_codecs, + const webrtc::FieldTrialsView* field_trials) { Codecs filtered_codecs; bool want_rtx = false; bool want_red = false; @@ -1070,7 +1077,7 @@ static Codecs MatchCodecPreference( if (found_codec != supported_codecs.end()) { typename Codecs::value_type found_codec_with_correct_pt; if (FindMatchingCodec(supported_codecs, codecs, *found_codec, - &found_codec_with_correct_pt)) { + &found_codec_with_correct_pt, field_trials)) { filtered_codecs.push_back(found_codec_with_correct_pt); std::string id = rtc::ToString(found_codec_with_correct_pt.id); // Search for the matching rtx or red codec. @@ -1114,7 +1121,8 @@ static Codecs MatchCodecPreference( // Compute the union of `codecs1` and `codecs2`. template std::vector ComputeCodecsUnion(const std::vector& codecs1, - const std::vector& codecs2) { + const std::vector& codecs2, + const webrtc::FieldTrialsView* field_trials) { std::vector all_codecs; UsedPayloadTypes used_payload_types; for (const C& codec : codecs1) { @@ -1125,7 +1133,7 @@ std::vector ComputeCodecsUnion(const std::vector& codecs1, // Use MergeCodecs to merge the second half of our list as it already checks // and fixes problems with duplicate payload types. - MergeCodecs(codecs2, &all_codecs, &used_payload_types); + MergeCodecs(codecs2, &all_codecs, &used_payload_types, field_trials); return all_codecs; } @@ -1353,15 +1361,17 @@ static bool SetCodecsInAnswer( const MediaSessionOptions& session_options, UniqueRandomIdGenerator* ssrc_generator, StreamParamsVec* current_streams, - MediaContentDescriptionImpl* answer) { + MediaContentDescriptionImpl* answer, + const webrtc::FieldTrialsView& field_trials) { std::vector negotiated_codecs; NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs, - media_description_options.codec_preferences.empty()); + media_description_options.codec_preferences.empty(), + &field_trials); answer->AddCodecs(negotiated_codecs); answer->set_protocol(offer->protocol()); if (!AddStreamParams(media_description_options.sender_options, session_options.rtcp_cname, ssrc_generator, - current_streams, answer)) { + current_streams, answer, field_trials)) { return false; // Something went seriously wrong. } return true; @@ -2040,16 +2050,19 @@ void MergeCodecsFromDescription( const std::vector& current_active_contents, AudioCodecs* audio_codecs, VideoCodecs* video_codecs, - UsedPayloadTypes* used_pltypes) { + UsedPayloadTypes* used_pltypes, + const webrtc::FieldTrialsView* field_trials) { for (const ContentInfo* content : current_active_contents) { if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) { const AudioContentDescription* audio = content->media_description()->as_audio(); - MergeCodecs(audio->codecs(), audio_codecs, used_pltypes); + MergeCodecs(audio->codecs(), audio_codecs, used_pltypes, + field_trials); } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) { const VideoContentDescription* video = content->media_description()->as_video(); - MergeCodecs(video->codecs(), video_codecs, used_pltypes); + MergeCodecs(video->codecs(), video_codecs, used_pltypes, + field_trials); } } } @@ -2064,16 +2077,20 @@ void MediaSessionDescriptionFactory::GetCodecsForOffer( const std::vector& current_active_contents, AudioCodecs* audio_codecs, VideoCodecs* video_codecs) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); // First - get all codecs from the current description if the media type // is used. Add them to `used_pltypes` so the payload type is not reused if a // new media type is added. UsedPayloadTypes used_pltypes; MergeCodecsFromDescription(current_active_contents, audio_codecs, - video_codecs, &used_pltypes); + video_codecs, &used_pltypes, field_trials); // Add our codecs that are not in the current description. - MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes); - MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes); + MergeCodecs(all_audio_codecs_, audio_codecs, &used_pltypes, + field_trials); + MergeCodecs(all_video_codecs_, video_codecs, &used_pltypes, + field_trials); } // Getting codecs for an answer involves these steps: @@ -2088,12 +2105,14 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const SessionDescription& remote_offer, AudioCodecs* audio_codecs, VideoCodecs* video_codecs) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); // First - get all codecs from the current description if the media type // is used. Add them to `used_pltypes` so the payload type is not reused if a // new media type is added. UsedPayloadTypes used_pltypes; MergeCodecsFromDescription(current_active_contents, audio_codecs, - video_codecs, &used_pltypes); + video_codecs, &used_pltypes, field_trials); // Second - filter out codecs that we don't support at all and should ignore. AudioCodecs filtered_offered_audio_codecs; @@ -2103,11 +2122,12 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const AudioContentDescription* audio = content.media_description()->as_audio(); for (const AudioCodec& offered_audio_codec : audio->codecs()) { - if (!FindMatchingCodec(audio->codecs(), - filtered_offered_audio_codecs, - offered_audio_codec, nullptr) && + if (!FindMatchingCodec( + audio->codecs(), filtered_offered_audio_codecs, + offered_audio_codec, nullptr, field_trials) && FindMatchingCodec(audio->codecs(), all_audio_codecs_, - offered_audio_codec, nullptr)) { + offered_audio_codec, nullptr, + field_trials)) { filtered_offered_audio_codecs.push_back(offered_audio_codec); } } @@ -2115,11 +2135,12 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( const VideoContentDescription* video = content.media_description()->as_video(); for (const VideoCodec& offered_video_codec : video->codecs()) { - if (!FindMatchingCodec(video->codecs(), - filtered_offered_video_codecs, - offered_video_codec, nullptr) && + if (!FindMatchingCodec( + video->codecs(), filtered_offered_video_codecs, + offered_video_codec, nullptr, field_trials) && FindMatchingCodec(video->codecs(), all_video_codecs_, - offered_video_codec, nullptr)) { + offered_video_codec, nullptr, + field_trials)) { filtered_offered_video_codecs.push_back(offered_video_codec); } } @@ -2129,9 +2150,9 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer( // Add codecs that are not in the current description but were in // `remote_offer`. MergeCodecs(filtered_offered_audio_codecs, audio_codecs, - &used_pltypes); + &used_pltypes, field_trials); MergeCodecs(filtered_offered_video_codecs, video_codecs, - &used_pltypes); + &used_pltypes, field_trials); } MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions @@ -2273,6 +2294,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( StreamParamsVec* current_streams, SessionDescription* desc, IceCredentialsIterator* ice_credentials) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); // Filter audio_codecs (which includes all codecs, with correctly remapped // payload types) based on transceiver direction. const AudioCodecs& supported_audio_codecs = @@ -2283,9 +2306,9 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( if (!media_description_options.codec_preferences.empty()) { // Add the codecs from the current transceiver's codec preferences. // They override any existing codecs from previous negotiations. - filtered_codecs = - MatchCodecPreference(media_description_options.codec_preferences, - audio_codecs, supported_audio_codecs); + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, audio_codecs, + supported_audio_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2296,7 +2319,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( current_content->media_description()->as_audio(); for (const AudioCodec& codec : acd->codecs()) { if (FindMatchingCodec(acd->codecs(), audio_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2305,9 +2328,10 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( AudioCodec found_codec; for (const AudioCodec& codec : supported_audio_codecs) { if (FindMatchingCodec(supported_audio_codecs, audio_codecs, - codec, &found_codec) && + codec, &found_codec, field_trials) && !FindMatchingCodec(supported_audio_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // Use the `found_codec` from `audio_codecs` because it has the // correctly mapped payload type. filtered_codecs.push_back(found_codec); @@ -2327,11 +2351,11 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( std::vector crypto_suites; GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options, &crypto_suites); - if (!CreateMediaContentOffer(media_description_options, session_options, - filtered_codecs, sdes_policy, - GetCryptos(current_content), crypto_suites, - audio_rtp_extensions, ssrc_generator_, - current_streams, audio.get())) { + if (!CreateMediaContentOffer( + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + audio_rtp_extensions, ssrc_generator_, current_streams, audio.get(), + transport_desc_factory_->trials())) { return false; } @@ -2363,6 +2387,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( StreamParamsVec* current_streams, SessionDescription* desc, IceCredentialsIterator* ice_credentials) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); // Filter video_codecs (which includes all codecs, with correctly remapped // payload types) based on transceiver direction. const VideoCodecs& supported_video_codecs = @@ -2373,9 +2399,9 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( if (!media_description_options.codec_preferences.empty()) { // Add the codecs from the current transceiver's codec preferences. // They override any existing codecs from previous negotiations. - filtered_codecs = - MatchCodecPreference(media_description_options.codec_preferences, - video_codecs, supported_video_codecs); + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, video_codecs, + supported_video_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2386,7 +2412,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( current_content->media_description()->as_video(); for (const VideoCodec& codec : vcd->codecs()) { if (FindMatchingCodec(vcd->codecs(), video_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2395,9 +2421,10 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( VideoCodec found_codec; for (const VideoCodec& codec : supported_video_codecs) { if (FindMatchingCodec(supported_video_codecs, video_codecs, - codec, &found_codec) && + codec, &found_codec, field_trials) && !FindMatchingCodec(supported_video_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // Use the `found_codec` from `video_codecs` because it has the // correctly mapped payload type. if (IsRtxCodec(codec)) { @@ -2409,9 +2436,9 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( // Find the codec we should be referencing and point to it. VideoCodec changed_referenced_codec; - if (FindMatchingCodec(supported_video_codecs, - filtered_codecs, *referenced_codec, - &changed_referenced_codec)) { + if (FindMatchingCodec( + supported_video_codecs, filtered_codecs, *referenced_codec, + &changed_referenced_codec, field_trials)) { found_codec.SetParam(kCodecParamAssociatedPayloadType, changed_referenced_codec.id); } @@ -2436,11 +2463,11 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( std::vector crypto_suites; GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options, &crypto_suites); - if (!CreateMediaContentOffer(media_description_options, session_options, - filtered_codecs, sdes_policy, - GetCryptos(current_content), crypto_suites, - video_rtp_extensions, ssrc_generator_, - current_streams, video.get())) { + if (!CreateMediaContentOffer( + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + video_rtp_extensions, ssrc_generator_, current_streams, video.get(), + transport_desc_factory_->trials())) { return false; } @@ -2557,6 +2584,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( StreamParamsVec* current_streams, SessionDescription* answer, IceCredentialsIterator* ice_credentials) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO)); const AudioContentDescription* offer_audio_description = offer_content->media_description()->as_audio(); @@ -2581,9 +2610,9 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( AudioCodecs filtered_codecs; if (!media_description_options.codec_preferences.empty()) { - filtered_codecs = - MatchCodecPreference(media_description_options.codec_preferences, - audio_codecs, supported_audio_codecs); + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, audio_codecs, + supported_audio_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2594,7 +2623,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( current_content->media_description()->as_audio(); for (const AudioCodec& codec : acd->codecs()) { if (FindMatchingCodec(acd->codecs(), audio_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2602,9 +2631,10 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( // Add other supported audio codecs. for (const AudioCodec& codec : supported_audio_codecs) { if (FindMatchingCodec(supported_audio_codecs, audio_codecs, - codec, nullptr) && + codec, nullptr, field_trials) && !FindMatchingCodec(supported_audio_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // We should use the local codec with local parameters and the codec id // would be correctly mapped in `NegotiateCodecs`. filtered_codecs.push_back(codec); @@ -2624,8 +2654,8 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( audio_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, - audio_answer.get())) { + ssrc_generator_, current_streams, audio_answer.get(), + transport_desc_factory_->trials())) { return false; } if (!CreateMediaContentAnswer( @@ -2673,6 +2703,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( StreamParamsVec* current_streams, SessionDescription* answer, IceCredentialsIterator* ice_credentials) const { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO)); const VideoContentDescription* offer_video_description = offer_content->media_description()->as_video(); @@ -2697,9 +2729,9 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( VideoCodecs filtered_codecs; if (!media_description_options.codec_preferences.empty()) { - filtered_codecs = - MatchCodecPreference(media_description_options.codec_preferences, - video_codecs, supported_video_codecs); + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, video_codecs, + supported_video_codecs, field_trials); } else { // Add the codecs from current content if it exists and is not rejected nor // recycled. @@ -2710,7 +2742,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( current_content->media_description()->as_video(); for (const VideoCodec& codec : vcd->codecs()) { if (FindMatchingCodec(vcd->codecs(), video_codecs, codec, - nullptr)) { + nullptr, field_trials)) { filtered_codecs.push_back(codec); } } @@ -2720,9 +2752,10 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( VideoCodecs other_video_codecs; for (const VideoCodec& codec : supported_video_codecs) { if (FindMatchingCodec(supported_video_codecs, video_codecs, - codec, nullptr) && + codec, nullptr, field_trials) && !FindMatchingCodec(supported_video_codecs, - filtered_codecs, codec, nullptr)) { + filtered_codecs, codec, nullptr, + field_trials)) { // We should use the local codec with local parameters and the codec id // would be correctly mapped in `NegotiateCodecs`. other_video_codecs.push_back(codec); @@ -2730,8 +2763,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( } // Use ComputeCodecsUnion to avoid having duplicate payload IDs - filtered_codecs = - ComputeCodecsUnion(filtered_codecs, other_video_codecs); + filtered_codecs = ComputeCodecsUnion( + filtered_codecs, other_video_codecs, field_trials); } if (session_options.raw_packetization_for_video) { @@ -2750,8 +2783,8 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( video_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_video_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, - video_answer.get())) { + ssrc_generator_, current_streams, video_answer.get(), + transport_desc_factory_->trials())) { return false; } if (!CreateMediaContentAnswer( @@ -2896,13 +2929,15 @@ bool MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer( } void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); audio_sendrecv_codecs_.clear(); all_audio_codecs_.clear(); // Compute the audio codecs union. for (const AudioCodec& send : audio_send_codecs_) { all_audio_codecs_.push_back(send); if (!FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_, - send, nullptr)) { + send, nullptr, field_trials)) { // It doesn't make sense to have an RTX codec we support sending but not // receiving. RTC_DCHECK(!IsRtxCodec(send)); @@ -2910,7 +2945,7 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { } for (const AudioCodec& recv : audio_recv_codecs_) { if (!FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_, - recv, nullptr)) { + recv, nullptr, field_trials)) { all_audio_codecs_.push_back(recv); } } @@ -2920,15 +2955,17 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { // expensive than decoding, and prioritizing a codec in the send list probably // means it's a codec we can handle efficiently. NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_, - &audio_sendrecv_codecs_, true); + &audio_sendrecv_codecs_, true, field_trials); } void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() { + const webrtc::FieldTrialsView* field_trials = + &transport_desc_factory_->trials(); video_sendrecv_codecs_.clear(); // Use ComputeCodecsUnion to avoid having duplicate payload IDs all_video_codecs_ = - ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_); + ComputeCodecsUnion(video_recv_codecs_, video_send_codecs_, field_trials); // Use NegotiateCodecs to merge our codec lists, since the operation is // essentially the same. Put send_codecs as the offered_codecs, which is the @@ -2936,7 +2973,7 @@ void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() { // expensive than decoding, and prioritizing a codec in the send list probably // means it's a codec we can handle efficiently. NegotiateCodecs(video_recv_codecs_, video_send_codecs_, - &video_sendrecv_codecs_, true); + &video_sendrecv_codecs_, true, field_trials); } bool IsMediaContent(const ContentInfo* content) { diff --git a/pc/media_session.h b/pc/media_session.h index aa24f015de..9a67ad1e3c 100644 --- a/pc/media_session.h +++ b/pc/media_session.h @@ -19,6 +19,7 @@ #include #include "api/crypto/crypto_options.h" +#include "api/field_trials_view.h" #include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" @@ -35,6 +36,13 @@ #include "pc/simulcast_description.h" #include "rtc_base/unique_id_generator.h" +namespace webrtc { + +// Forward declaration due to circular dependecy. +class ConnectionContext; + +} // namespace webrtc + namespace cricket { class ChannelManager; diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc index cfe9ac8a31..3ca6c8580f 100644 --- a/pc/media_session_unittest.cc +++ b/pc/media_session_unittest.cc @@ -10,32 +10,42 @@ #include "pc/media_session.h" +#include + #include +#include +#include #include #include -#include +#include #include #include "absl/algorithm/container.h" -#include "absl/memory/memory.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "api/candidate.h" +#include "api/crypto_params.h" #include "media/base/codec.h" +#include "media/base/media_constants.h" #include "media/base/test_utils.h" #include "media/sctp/sctp_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/rtp_media_utils.h" -#include "pc/srtp_filter.h" +#include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/fake_ssl_identity.h" -#include "rtc_base/gunit.h" -#include "rtc_base/message_digest.h" -#include "rtc_base/ssl_adapter.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/unique_id_generator.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" #define ASSERT_CRYPTO(cd, s, cs) \ ASSERT_EQ(s, cd->cryptos().size()); \ @@ -421,7 +431,10 @@ void PreferGcmCryptoParameters(CryptoParamsVec* cryptos) { class MediaSessionDescriptionFactoryTest : public ::testing::Test { public: MediaSessionDescriptionFactoryTest() - : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) { + : tdf1_(field_trials), + tdf2_(field_trials), + f1_(&tdf1_, &ssrc_generator1), + f2_(&tdf2_, &ssrc_generator2) { f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), MAKE_VECTOR(kAudioCodecs1)); f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), @@ -781,12 +794,13 @@ class MediaSessionDescriptionFactoryTest : public ::testing::Test { } protected: + webrtc::test::ScopedKeyValueConfig field_trials; UniqueRandomIdGenerator ssrc_generator1; UniqueRandomIdGenerator ssrc_generator2; - MediaSessionDescriptionFactory f1_; - MediaSessionDescriptionFactory f2_; TransportDescriptionFactory tdf1_; TransportDescriptionFactory tdf2_; + MediaSessionDescriptionFactory f1_; + MediaSessionDescriptionFactory f2_; }; // Create a typical audio offer, and ensure it matches what we expect. @@ -3250,8 +3264,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) { // Test that, when the FlexFEC codec is added, a FlexFEC ssrc is created // together with a FEC-FR grouping. Guarded by WebRTC-FlexFEC-03 trial. TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FlexFEC-03/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials, "WebRTC-FlexFEC-03/Enabled/"); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, kActive, @@ -3293,8 +3307,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { // TODO(brandtr): Remove this test when we support simulcast, either through // multiple FlexfecSenders, or through multistream protection. TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) { - webrtc::test::ScopedFieldTrials override_field_trials( - "WebRTC-FlexFEC-03/Enabled/"); + webrtc::test::ScopedKeyValueConfig override_field_trials( + field_trials, "WebRTC-FlexFEC-03/Enabled/"); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, kActive, @@ -4320,7 +4334,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, class MediaProtocolTest : public ::testing::TestWithParam { public: MediaProtocolTest() - : f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) { + : tdf1_(field_trials_), + tdf2_(field_trials_), + f1_(&tdf1_, &ssrc_generator1), + f2_(&tdf2_, &ssrc_generator2) { f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), MAKE_VECTOR(kAudioCodecs1)); f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), @@ -4340,10 +4357,11 @@ class MediaProtocolTest : public ::testing::TestWithParam { } protected: - MediaSessionDescriptionFactory f1_; - MediaSessionDescriptionFactory f2_; + webrtc::test::ScopedKeyValueConfig field_trials_; TransportDescriptionFactory tdf1_; TransportDescriptionFactory tdf2_; + MediaSessionDescriptionFactory f1_; + MediaSessionDescriptionFactory f2_; UniqueRandomIdGenerator ssrc_generator1; UniqueRandomIdGenerator ssrc_generator2; }; @@ -4379,14 +4397,15 @@ INSTANTIATE_TEST_SUITE_P(MediaProtocolDtlsPatternTest, ::testing::ValuesIn(kMediaProtocolsDtls)); TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) { - TransportDescriptionFactory tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory tdf(field_trials); UniqueRandomIdGenerator ssrc_generator; MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator); std::vector send_codecs = MAKE_VECTOR(kAudioCodecs1); std::vector recv_codecs = MAKE_VECTOR(kAudioCodecs2); // The merged list of codecs should contain any send codecs that are also - // nominally in the recieve codecs list. Payload types should be picked from + // nominally in the receive codecs list. Payload types should be picked from // the send codecs and a number-of-channels of 0 and 1 should be equivalent // (set to 1). This equals what happens when the send codecs are used in an // offer and the receive codecs are used in the following answer. @@ -4435,13 +4454,14 @@ namespace { // Compare the two vectors of codecs ignoring the payload type. template bool CodecsMatch(const std::vector& codecs1, - const std::vector& codecs2) { + const std::vector& codecs2, + const webrtc::FieldTrialsView* field_trials) { if (codecs1.size() != codecs2.size()) { return false; } for (size_t i = 0; i < codecs1.size(); ++i) { - if (!codecs1[i].Matches(codecs2[i])) { + if (!codecs1[i].Matches(codecs2[i], field_trials)) { return false; } } @@ -4449,7 +4469,8 @@ bool CodecsMatch(const std::vector& codecs1, } void TestAudioCodecsOffer(RtpTransceiverDirection direction) { - TransportDescriptionFactory tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory tdf(field_trials); UniqueRandomIdGenerator ssrc_generator; MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator); const std::vector send_codecs = MAKE_VECTOR(kAudioCodecs1); @@ -4483,11 +4504,14 @@ void TestAudioCodecsOffer(RtpTransceiverDirection direction) { // might eventually be used anything, but we don't know more at this // moment. if (acd->direction() == RtpTransceiverDirection::kSendOnly) { - EXPECT_TRUE(CodecsMatch(send_codecs, acd->codecs())); + EXPECT_TRUE( + CodecsMatch(send_codecs, acd->codecs(), &field_trials)); } else if (acd->direction() == RtpTransceiverDirection::kRecvOnly) { - EXPECT_TRUE(CodecsMatch(recv_codecs, acd->codecs())); + EXPECT_TRUE( + CodecsMatch(recv_codecs, acd->codecs(), &field_trials)); } else { - EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs())); + EXPECT_TRUE(CodecsMatch(sendrecv_codecs, acd->codecs(), + &field_trials)); } } } @@ -4546,11 +4570,13 @@ std::vector VectorFromIndices(const T* array, const int (&indices)[IDXS]) { void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, RtpTransceiverDirection answer_direction, bool add_legacy_stream) { - TransportDescriptionFactory offer_tdf; - TransportDescriptionFactory answer_tdf; + webrtc::test::ScopedKeyValueConfig field_trials; + TransportDescriptionFactory offer_tdf(field_trials); + TransportDescriptionFactory answer_tdf(field_trials); UniqueRandomIdGenerator ssrc_generator1, ssrc_generator2; MediaSessionDescriptionFactory offer_factory(&offer_tdf, &ssrc_generator1); MediaSessionDescriptionFactory answer_factory(&answer_tdf, &ssrc_generator2); + offer_factory.set_audio_codecs( VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs), VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs)); diff --git a/pc/media_stream.cc b/pc/media_stream.cc index 6fe308827c..df230e461a 100644 --- a/pc/media_stream.cc +++ b/pc/media_stream.cc @@ -13,7 +13,6 @@ #include #include -#include #include "rtc_base/checks.h" #include "rtc_base/ref_counted_object.h" @@ -37,21 +36,19 @@ rtc::scoped_refptr MediaStream::Create(const std::string& id) { MediaStream::MediaStream(const std::string& id) : id_(id) {} -bool MediaStream::AddTrack(AudioTrackInterface* track) { - return AddTrack( - &audio_tracks_, rtc::scoped_refptr(track)); +bool MediaStream::AddTrack(rtc::scoped_refptr track) { + return AddTrack(&audio_tracks_, track); } -bool MediaStream::AddTrack(VideoTrackInterface* track) { - return AddTrack( - &video_tracks_, rtc::scoped_refptr(track)); +bool MediaStream::AddTrack(rtc::scoped_refptr track) { + return AddTrack(&video_tracks_, track); } -bool MediaStream::RemoveTrack(AudioTrackInterface* track) { +bool MediaStream::RemoveTrack(rtc::scoped_refptr track) { return RemoveTrack(&audio_tracks_, track); } -bool MediaStream::RemoveTrack(VideoTrackInterface* track) { +bool MediaStream::RemoveTrack(rtc::scoped_refptr track) { return RemoveTrack(&video_tracks_, track); } @@ -83,8 +80,9 @@ bool MediaStream::AddTrack(TrackVector* tracks, } template -bool MediaStream::RemoveTrack(TrackVector* tracks, - MediaStreamTrackInterface* track) { +bool MediaStream::RemoveTrack( + TrackVector* tracks, + rtc::scoped_refptr track) { RTC_DCHECK(tracks != NULL); if (!track) return false; diff --git a/pc/media_stream.h b/pc/media_stream.h index 70e58f9760..c033cf6f35 100644 --- a/pc/media_stream.h +++ b/pc/media_stream.h @@ -27,10 +27,10 @@ class MediaStream : public Notifier { std::string id() const override { return id_; } - bool AddTrack(AudioTrackInterface* track) override; - bool AddTrack(VideoTrackInterface* track) override; - bool RemoveTrack(AudioTrackInterface* track) override; - bool RemoveTrack(VideoTrackInterface* track) override; + bool AddTrack(rtc::scoped_refptr track) override; + bool AddTrack(rtc::scoped_refptr track) override; + bool RemoveTrack(rtc::scoped_refptr track) override; + bool RemoveTrack(rtc::scoped_refptr track) override; rtc::scoped_refptr FindAudioTrack( const std::string& track_id) override; rtc::scoped_refptr FindVideoTrack( @@ -46,7 +46,8 @@ class MediaStream : public Notifier { template bool AddTrack(TrackVector* Tracks, rtc::scoped_refptr track); template - bool RemoveTrack(TrackVector* Tracks, MediaStreamTrackInterface* track); + bool RemoveTrack(TrackVector* Tracks, + rtc::scoped_refptr track); const std::string id_; AudioTrackVector audio_tracks_; diff --git a/pc/media_stream_observer.cc b/pc/media_stream_observer.cc index 28caccf5d5..6264a7657a 100644 --- a/pc/media_stream_observer.cc +++ b/pc/media_stream_observer.cc @@ -54,7 +54,7 @@ void MediaStreamObserver::OnChanged() { [cached_track](const AudioTrackVector::value_type& new_track) { return new_track->id() == cached_track->id(); })) { - audio_track_removed_callback_(cached_track.get(), stream_); + audio_track_removed_callback_(cached_track.get(), stream_.get()); } } @@ -65,7 +65,7 @@ void MediaStreamObserver::OnChanged() { [new_track](const AudioTrackVector::value_type& cached_track) { return new_track->id() == cached_track->id(); })) { - audio_track_added_callback_(new_track.get(), stream_); + audio_track_added_callback_(new_track.get(), stream_.get()); } } @@ -76,7 +76,7 @@ void MediaStreamObserver::OnChanged() { [cached_track](const VideoTrackVector::value_type& new_track) { return new_track->id() == cached_track->id(); })) { - video_track_removed_callback_(cached_track.get(), stream_); + video_track_removed_callback_(cached_track.get(), stream_.get()); } } @@ -87,7 +87,7 @@ void MediaStreamObserver::OnChanged() { [new_track](const VideoTrackVector::value_type& cached_track) { return new_track->id() == cached_track->id(); })) { - video_track_added_callback_(new_track.get(), stream_); + video_track_added_callback_(new_track.get(), stream_.get()); } } diff --git a/pc/media_stream_observer.h b/pc/media_stream_observer.h index 4c4f22168b..83bbd20994 100644 --- a/pc/media_stream_observer.h +++ b/pc/media_stream_observer.h @@ -34,7 +34,7 @@ class MediaStreamObserver : public ObserverInterface { video_track_removed_callback); ~MediaStreamObserver() override; - const MediaStreamInterface* stream() const { return stream_; } + const MediaStreamInterface* stream() const { return stream_.get(); } void OnChanged() override; diff --git a/pc/media_stream_proxy.h b/pc/media_stream_proxy.h index 36069a4369..3e263bfd8b 100644 --- a/pc/media_stream_proxy.h +++ b/pc/media_stream_proxy.h @@ -31,10 +31,10 @@ PROXY_METHOD1(rtc::scoped_refptr, PROXY_METHOD1(rtc::scoped_refptr, FindVideoTrack, const std::string&) -PROXY_METHOD1(bool, AddTrack, AudioTrackInterface*) -PROXY_METHOD1(bool, AddTrack, VideoTrackInterface*) -PROXY_METHOD1(bool, RemoveTrack, AudioTrackInterface*) -PROXY_METHOD1(bool, RemoveTrack, VideoTrackInterface*) +PROXY_METHOD1(bool, AddTrack, rtc::scoped_refptr) +PROXY_METHOD1(bool, AddTrack, rtc::scoped_refptr) +PROXY_METHOD1(bool, RemoveTrack, rtc::scoped_refptr) +PROXY_METHOD1(bool, RemoveTrack, rtc::scoped_refptr) PROXY_METHOD1(void, RegisterObserver, ObserverInterface*) PROXY_METHOD1(void, UnregisterObserver, ObserverInterface*) END_PROXY_MAP(MediaStream) diff --git a/pc/media_stream_unittest.cc b/pc/media_stream_unittest.cc index 6ce8de9a1a..a1146a347a 100644 --- a/pc/media_stream_unittest.cc +++ b/pc/media_stream_unittest.cc @@ -12,8 +12,6 @@ #include -#include - #include "pc/audio_track.h" #include "pc/test/fake_video_track_source.h" #include "pc/video_track.h" @@ -120,7 +118,7 @@ TEST_F(MediaStreamTest, GetTrackInfo) { } TEST_F(MediaStreamTest, RemoveTrack) { - MockObserver observer(stream_); + MockObserver observer(stream_.get()); EXPECT_CALL(observer, OnChanged()).Times(Exactly(2)); @@ -135,8 +133,8 @@ TEST_F(MediaStreamTest, RemoveTrack) { EXPECT_EQ(0u, stream_->GetVideoTracks().size()); EXPECT_EQ(0u, stream_->GetVideoTracks().size()); - EXPECT_FALSE(stream_->RemoveTrack(static_cast(NULL))); - EXPECT_FALSE(stream_->RemoveTrack(static_cast(NULL))); + EXPECT_FALSE(stream_->RemoveTrack(rtc::scoped_refptr())); + EXPECT_FALSE(stream_->RemoveTrack(rtc::scoped_refptr())); } TEST_F(MediaStreamTest, ChangeVideoTrack) { diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index a315ac510f..f914fc8365 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "absl/algorithm/container.h" @@ -26,7 +25,6 @@ #include "api/jsep_ice_candidate.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/uma_metrics.h" #include "api/video/video_codec_constants.h" #include "call/audio_state.h" @@ -44,7 +42,7 @@ #include "p2p/base/p2p_constants.h" #include "p2p/base/p2p_transport_channel.h" #include "p2p/base/transport_info.h" -#include "pc/channel.h" +#include "pc/channel_manager.h" #include "pc/ice_server_parsing.h" #include "pc/rtp_receiver.h" #include "pc/rtp_receiver_proxy.h" @@ -282,8 +280,12 @@ bool DtlsEnabled(const PeerConnectionInterface::RTCConfiguration& configuration, bool default_enabled = (dependencies.cert_generator || !configuration.certificates.empty()); +#if defined(WEBRTC_FUCHSIA) // The `configuration` can override the default value. return configuration.enable_dtls_srtp.value_or(default_enabled); +#else + return default_enabled; +#endif } } // namespace @@ -305,7 +307,9 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( bool disable_link_local_networks; absl::optional screencast_min_bitrate; absl::optional combined_audio_video_bwe; +#if defined(WEBRTC_FUCHSIA) absl::optional enable_dtls_srtp; +#endif TcpCandidatePolicy tcp_candidate_policy; CandidateNetworkPolicy candidate_network_policy; int audio_jitter_buffer_max_packets; @@ -374,7 +378,9 @@ bool PeerConnectionInterface::RTCConfiguration::operator==( disable_link_local_networks == o.disable_link_local_networks && screencast_min_bitrate == o.screencast_min_bitrate && combined_audio_video_bwe == o.combined_audio_video_bwe && +#if defined(WEBRTC_FUCHSIA) enable_dtls_srtp == o.enable_dtls_srtp && +#endif ice_candidate_pool_size == o.ice_candidate_pool_size && prune_turn_ports == o.prune_turn_ports && turn_port_prune_policy == o.turn_port_prune_policy && @@ -503,6 +509,7 @@ PeerConnection::PeerConnection( PeerConnectionDependencies& dependencies, bool dtls_enabled) : context_(context), + trials_(std::move(dependencies.trials), &context->field_trials()), options_(options), observer_(dependencies.observer), is_unified_plan_(is_unified_plan), @@ -637,7 +644,7 @@ RTCError PeerConnection::Initialize( stats_collector_ = RTCStatsCollector::Create(this); sdp_handler_ = SdpOfferAnswerHandler::Create(this, configuration, - dependencies, context_); + dependencies, context_.get()); rtp_manager_ = std::make_unique( IsUnifiedPlan(), signaling_thread(), worker_thread(), channel_manager(), @@ -651,11 +658,13 @@ RTCError PeerConnection::Initialize( rtp_manager()->transceivers()->Add( RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO, channel_manager()))); + rtc::make_ref_counted(cricket::MEDIA_TYPE_AUDIO, + channel_manager()))); rtp_manager()->transceivers()->Add( RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO, channel_manager()))); + rtc::make_ref_counted(cricket::MEDIA_TYPE_VIDEO, + channel_manager()))); } int delay_ms = configuration.report_usage_pattern_delay_ms @@ -711,6 +720,8 @@ JsepTransportController* PeerConnection::InitializeTransportController_n( } }; + config.field_trials = trials_.get(); + transport_controller_.reset( new JsepTransportController(network_thread(), port_allocator_.get(), async_dns_resolver_factory_.get(), config)); @@ -818,11 +829,16 @@ bool PeerConnection::AddStream(MediaStreamInterface* local_stream) { RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan " "SdpSemantics. Please use AddTrack instead."; TRACE_EVENT0("webrtc", "PeerConnection::AddStream"); + if (!ConfiguredForMedia()) { + RTC_LOG(LS_ERROR) << "AddStream: Not configured for media"; + return false; + } return sdp_handler_->AddStream(local_stream); } void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) { RTC_DCHECK_RUN_ON(signaling_thread()); + RTC_DCHECK(ConfiguredForMedia()); RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified " "Plan SdpSemantics. Please use RemoveTrack " "instead."; @@ -835,6 +851,10 @@ RTCErrorOr> PeerConnection::AddTrack( const std::vector& stream_ids) { RTC_DCHECK_RUN_ON(signaling_thread()); TRACE_EVENT0("webrtc", "PeerConnection::AddTrack"); + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } if (!track) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); } @@ -847,7 +867,7 @@ RTCErrorOr> PeerConnection::AddTrack( LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, "PeerConnection is closed."); } - if (rtp_manager()->FindSenderForTrack(track)) { + if (rtp_manager()->FindSenderForTrack(track.get())) { LOG_AND_RETURN_ERROR( RTCErrorType::INVALID_PARAMETER, "Sender already exists for track " + track->id() + "."); @@ -855,7 +875,7 @@ RTCErrorOr> PeerConnection::AddTrack( auto sender_or_error = rtp_manager()->AddTrack(track, stream_ids); if (sender_or_error.ok()) { sdp_handler_->UpdateNegotiationNeeded(); - stats_->AddTrack(track); + stats_->AddTrack(track.get()); } return sender_or_error; } @@ -863,6 +883,10 @@ RTCErrorOr> PeerConnection::AddTrack( RTCError PeerConnection::RemoveTrackOrError( rtc::scoped_refptr sender) { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } if (!sender) { LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); } @@ -887,11 +911,11 @@ RTCError PeerConnection::RemoveTrackOrError( bool removed; if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { removed = rtp_manager()->GetAudioTransceiver()->internal()->RemoveSender( - sender); + sender.get()); } else { RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type()); removed = rtp_manager()->GetVideoTransceiver()->internal()->RemoveSender( - sender); + sender.get()); } if (!removed) { LOG_AND_RETURN_ERROR( @@ -912,6 +936,11 @@ PeerConnection::FindTransceiverBySender( RTCErrorOr> PeerConnection::AddTransceiver( rtc::scoped_refptr track) { + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } + return AddTransceiver(track, RtpTransceiverInit()); } @@ -933,6 +962,10 @@ PeerConnection::AddTransceiver( rtc::scoped_refptr track, const RtpTransceiverInit& init) { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } RTC_CHECK(IsUnifiedPlan()) << "AddTransceiver is only available with Unified Plan SdpSemantics"; if (!track) { @@ -959,6 +992,10 @@ RTCErrorOr> PeerConnection::AddTransceiver(cricket::MediaType media_type, const RtpTransceiverInit& init) { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } RTC_CHECK(IsUnifiedPlan()) << "AddTransceiver is only available with Unified Plan SdpSemantics"; if (!(media_type == cricket::MEDIA_TYPE_AUDIO || @@ -976,6 +1013,10 @@ PeerConnection::AddTransceiver( const RtpTransceiverInit& init, bool update_negotiation_needed) { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION, + "Not configured for media"); + } RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || media_type == cricket::MEDIA_TYPE_VIDEO)); if (track) { @@ -1084,6 +1125,10 @@ rtc::scoped_refptr PeerConnection::CreateSender( const std::string& kind, const std::string& stream_id) { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + RTC_LOG(LS_ERROR) << "Not configured for media"; + return nullptr; + } RTC_CHECK(!IsUnifiedPlan()) << "CreateSender is not available with Unified " "Plan SdpSemantics. Please use AddTransceiver " "instead."; @@ -1548,10 +1593,10 @@ RTCError PeerConnection::SetConfiguration( if (transceiver->media_type() != cricket::MEDIA_TYPE_VIDEO) continue; - auto* video_channel = static_cast( - transceiver->internal()->channel()); + auto* video_channel = transceiver->internal()->channel(); if (video_channel) - channels.push_back(video_channel->media_channel()); + channels.push_back(static_cast( + video_channel->media_channel())); } worker_thread()->Invoke( @@ -1666,8 +1711,8 @@ void PeerConnection::AddAdaptationResource( call_->AddAdaptationResource(resource); } -cricket::ChannelManager* PeerConnection::channel_manager() { - return context_->channel_manager(); +bool PeerConnection::ConfiguredForMedia() const { + return context_->channel_manager()->media_engine(); } bool PeerConnection::StartRtcEventLog(std::unique_ptr output, @@ -1682,8 +1727,7 @@ bool PeerConnection::StartRtcEventLog(std::unique_ptr output, bool PeerConnection::StartRtcEventLog( std::unique_ptr output) { int64_t output_period_ms = webrtc::RtcEventLog::kImmediateOutput; - if (absl::StartsWith(context_->trials().Lookup("WebRTC-RtcEventLogNewFormat"), - "Enabled")) { + if (trials().IsEnabled("WebRTC-RtcEventLogNewFormat")) { output_period_ms = 5000; } return StartRtcEventLog(std::move(output), output_period_ms); @@ -2017,10 +2061,6 @@ void PeerConnection::OnSctpDataChannelClosed(DataChannelInterface* channel) { static_cast(channel)); } -SctpDataChannel* PeerConnection::FindDataChannelBySid(int sid) const { - return data_channel_controller_.FindDataChannelBySid(sid); -} - PeerConnection::InitializePortAllocatorResult PeerConnection::InitializePortAllocator_n( const cricket::ServerAddresses& stun_servers, @@ -2039,8 +2079,7 @@ PeerConnection::InitializePortAllocator_n( // by experiment. if (configuration.disable_ipv6) { port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); - } else if (absl::StartsWith(context_->trials().Lookup("WebRTC-IPv6Default"), - "Disabled")) { + } else if (trials().IsDisabled("WebRTC-IPv6Default")) { port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); } if (configuration.disable_ipv6_on_wifi) { @@ -2136,16 +2175,6 @@ void PeerConnection::StopRtcEventLog_w() { } } -cricket::ChannelInterface* PeerConnection::GetChannel(const std::string& mid) { - for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->mid() == mid) { - return channel; - } - } - return nullptr; -} - bool PeerConnection::GetSctpSslRole(rtc::SSLRole* role) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!local_description() || !remote_description()) { @@ -2859,9 +2888,11 @@ bool PeerConnection::OnTransportChanged( DataChannelTransportInterface* data_channel_transport) { RTC_DCHECK_RUN_ON(network_thread()); bool ret = true; - auto base_channel = GetChannel(mid); - if (base_channel) { - ret = base_channel->SetRtpTransport(rtp_transport); + for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && channel->mid() == mid) { + ret = channel->SetRtpTransport(rtp_transport); + } } if (mid == sctp_mid_n_) { diff --git a/pc/peer_connection.h b/pc/peer_connection.h index 4855d32be1..2907cb565b 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -14,33 +14,28 @@ #include #include -#include #include #include #include #include -#include #include #include "absl/types/optional.h" #include "api/adaptation/resource.h" #include "api/async_dns_resolver.h" -#include "api/async_resolver_factory.h" -#include "api/audio_options.h" #include "api/candidate.h" #include "api/crypto/crypto_options.h" #include "api/data_channel_interface.h" #include "api/dtls_transport_interface.h" +#include "api/field_trials_view.h" #include "api/ice_transport_interface.h" #include "api/jsep.h" #include "api/media_stream_interface.h" #include "api/media_types.h" -#include "api/packet_socket_factory.h" #include "api/peer_connection_interface.h" #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log_output.h" -#include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" #include "api/rtp_transceiver_interface.h" @@ -54,17 +49,12 @@ #include "api/transport/data_channel_transport_interface.h" #include "api/transport/enums.h" #include "api/turn_customizer.h" -#include "api/video/video_bitrate_allocator_factory.h" #include "call/call.h" -#include "media/base/media_channel.h" -#include "media/base/media_engine.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/transport_description.h" -#include "pc/channel.h" #include "pc/channel_interface.h" -#include "pc/channel_manager.h" #include "pc/connection_context.h" #include "pc/data_channel_controller.h" #include "pc/data_channel_utils.h" @@ -73,23 +63,18 @@ #include "pc/peer_connection_internal.h" #include "pc/peer_connection_message_handler.h" #include "pc/rtc_stats_collector.h" -#include "pc/rtp_receiver.h" -#include "pc/rtp_sender.h" #include "pc/rtp_transceiver.h" #include "pc/rtp_transmission_manager.h" #include "pc/rtp_transport_internal.h" #include "pc/sctp_data_channel.h" -#include "pc/sctp_transport.h" #include "pc/sdp_offer_answer.h" #include "pc/session_description.h" #include "pc/stats_collector.h" -#include "pc/stream_collection.h" #include "pc/transceiver_list.h" #include "pc/transport_stats.h" #include "pc/usage_pattern.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/network/sent_packet.h" #include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_stream_adapter.h" @@ -97,9 +82,12 @@ #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" -#include "rtc_base/unique_id_generator.h" #include "rtc_base/weak_ptr.h" +namespace cricket { +class ChannelManager; +} + namespace webrtc { // PeerConnection is the implementation of the PeerConnection object as defined @@ -355,7 +343,6 @@ class PeerConnection : public PeerConnectionInternal, const RtpTransmissionManager* rtp_manager() const override { return rtp_manager_.get(); } - cricket::ChannelManager* channel_manager(); JsepTransportController* transport_controller_s() override { RTC_DCHECK_RUN_ON(signaling_thread()); @@ -442,8 +429,10 @@ class PeerConnection : public PeerConnectionInternal, bool SetupDataChannelTransport_n(const std::string& mid) override RTC_RUN_ON(network_thread()); void TeardownDataChannelTransport_n() override RTC_RUN_ON(network_thread()); - cricket::ChannelInterface* GetChannel(const std::string& mid) - RTC_RUN_ON(network_thread()); + + const FieldTrialsView& trials() const override { return *trials_; } + + bool ConfiguredForMedia() const; // Functions made public for testing. void ReturnHistogramVeryQuicklyForTesting() { @@ -507,11 +496,6 @@ class PeerConnection : public PeerConnectionInternal, void OnNegotiationNeeded(); - // Returns the specified SCTP DataChannel in sctp_data_channels_, - // or nullptr if not found. - SctpDataChannel* FindDataChannelBySid(int sid) const - RTC_RUN_ON(signaling_thread()); - // Called when first configuring the port allocator. struct InitializePortAllocatorResult { bool enable_ipv6; @@ -609,7 +593,17 @@ class PeerConnection : public PeerConnectionInternal, int64_t packet_time_us)> InitializeRtcpCallback(); + cricket::ChannelManager* channel_manager() { + return context_->channel_manager(); + } + const rtc::scoped_refptr context_; + // Field trials active for this PeerConnection is the first of: + // a) Specified in PeerConnectionDependencies (owned). + // b) Accessed via ConnectionContext (e.g PeerConnectionFactoryDependencies> + // c) Created as Default (FieldTrialBasedConfig). + const webrtc::AlwaysValidPointer + trials_; const PeerConnectionFactoryInterface::Options options_; PeerConnectionObserver* observer_ RTC_GUARDED_BY(signaling_thread()) = nullptr; diff --git a/pc/peer_connection_adaptation_integrationtest.cc b/pc/peer_connection_adaptation_integrationtest.cc index dfb12971b4..5dc26e02ca 100644 --- a/pc/peer_connection_adaptation_integrationtest.cc +++ b/pc/peer_connection_adaptation_integrationtest.cc @@ -8,12 +8,22 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include +#include +#include "absl/types/optional.h" +#include "api/adaptation/resource.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/video/video_source_interface.h" #include "call/adaptation/test/fake_resource.h" #include "pc/test/fake_periodic_video_source.h" #include "pc/test/fake_periodic_video_track_source.h" @@ -22,6 +32,7 @@ #include "rtc_base/gunit.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/thread.h" +#include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "test/gtest.h" @@ -54,7 +65,7 @@ TrackWithPeriodicSource CreateTrackWithPeriodicSource( periodic_track_source_config, /* remote */ false); TrackWithPeriodicSource track_with_source; track_with_source.track = - factory->CreateVideoTrack("PeriodicTrack", periodic_track_source); + factory->CreateVideoTrack("PeriodicTrack", periodic_track_source.get()); track_with_source.periodic_track_source = periodic_track_source; return track_with_source; } diff --git a/pc/peer_connection_bundle_unittest.cc b/pc/peer_connection_bundle_unittest.cc index e5eb6c4e18..fed4930f43 100644 --- a/pc/peer_connection_bundle_unittest.cc +++ b/pc/peer_connection_bundle_unittest.cc @@ -8,21 +8,59 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/candidate.h" #include "api/create_peerconnection_factory.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/stats/rtc_stats.h" +#include "api/stats/rtc_stats_report.h" +#include "api/stats/rtcstats_objects.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "p2p/base/fake_port_allocator.h" -#include "p2p/base/test_stun_server.h" +#include "media/base/stream_params.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" #include "p2p/client/basic_port_allocator.h" -#include "pc/media_session.h" +#include "pc/channel.h" #include "pc/peer_connection.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" +#include "pc/rtp_transceiver.h" +#include "pc/rtp_transport_internal.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/network.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -54,13 +92,14 @@ constexpr int kDefaultTimeout = 10000; class FakeNetworkManagerWithNoAnyNetwork : public rtc::FakeNetworkManager { public: - void GetAnyAddressNetworks(NetworkList* networks) override { + std::vector GetAnyAddressNetworks() override { // This function allocates networks that are owned by the // NetworkManager. But some tests assume that they can release // all networks independent of the network manager. // In order to prevent use-after-free issues, don't allow this // function to have any effect when run in tests. RTC_LOG(LS_INFO) << "FakeNetworkManager::GetAnyAddressNetworks ignored"; + return {}; } }; @@ -193,14 +232,16 @@ class PeerConnectionBundleBaseTest : public ::testing::Test { auto observer = std::make_unique(); RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - auto pc = pc_factory_->CreatePeerConnection( - modified_config, std::move(port_allocator), nullptr, observer.get()); - if (!pc) { + PeerConnectionDependencies pc_dependencies(observer.get()); + pc_dependencies.allocator = std::move(port_allocator); + auto result = pc_factory_->CreatePeerConnectionOrError( + modified_config, std::move(pc_dependencies)); + if (!result.ok()) { return nullptr; } auto wrapper = std::make_unique( - pc_factory_, pc, std::move(observer)); + pc_factory_, result.MoveValue(), std::move(observer)); wrapper->set_network(fake_network); return wrapper; } @@ -450,7 +491,7 @@ TEST_P(PeerConnectionBundleMatrixTest, INSTANTIATE_TEST_SUITE_P( PeerConnectionBundleTest, PeerConnectionBundleMatrixTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_tuple(BundlePolicy::kBundlePolicyBalanced, BundleIncluded::kBundleInAnswer, false, @@ -853,7 +894,7 @@ TEST_P(PeerConnectionBundleTest, RemoveContentFromBundleGroup) { INSTANTIATE_TEST_SUITE_P(PeerConnectionBundleTest, PeerConnectionBundleTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); // According to RFC5888, if an endpoint understands the semantics of an diff --git a/pc/peer_connection_crypto_unittest.cc b/pc/peer_connection_crypto_unittest.cc index 394203cb02..d87d0b0d1e 100644 --- a/pc/peer_connection_crypto_unittest.cc +++ b/pc/peer_connection_crypto_unittest.cc @@ -8,17 +8,47 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" +#include "api/crypto/crypto_options.h" +#include "api/crypto_params.h" +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -71,16 +101,18 @@ class PeerConnectionCryptoBaseTest : public ::testing::Test { auto observer = std::make_unique(); RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - auto pc = pc_factory_->CreatePeerConnection( - modified_config, std::move(fake_port_allocator), std::move(cert_gen), - observer.get()); - if (!pc) { + PeerConnectionDependencies pc_dependencies(observer.get()); + pc_dependencies.allocator = std::move(fake_port_allocator); + pc_dependencies.cert_generator = std::move(cert_gen); + auto result = pc_factory_->CreatePeerConnectionOrError( + modified_config, std::move(pc_dependencies)); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); - return std::make_unique(pc_factory_, pc, - std::move(observer)); + observer->SetPeerConnectionInterface(result.value().get()); + return std::make_unique( + pc_factory_, result.MoveValue(), std::move(observer)); } // Accepts the same arguments as CreatePeerConnection and adds default audio @@ -181,7 +213,6 @@ SdpContentMutator RemoveDtlsFingerprint() { // no SDES cryptos. TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto offer = caller->CreateOffer(); @@ -195,7 +226,6 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsEnabled) { } TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto callee = CreatePeerConnectionWithAudioVideo(config); @@ -210,6 +240,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsEnabled) { answer->description())); } +#if defined(WEBRTC_FUCHSIA) // When DTLS is disabled, the SDP offer/answer should include SDES cryptos and // should not have a DTLS fingerprint. TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) { @@ -226,6 +257,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenDtlsDisabled) { EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf), offer->description())); } + TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenDtlsDisabled) { RTCConfiguration config; config.enable_dtls_srtp.emplace(false); @@ -263,6 +295,7 @@ TEST_P(PeerConnectionCryptoTest, CorrectCryptoInOfferWhenEncryptionDisabled) { EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf), offer->description())); } + TEST_P(PeerConnectionCryptoTest, CorrectCryptoInAnswerWhenEncryptionDisabled) { PeerConnectionFactoryInterface::Options options; options.disable_encryption = true; @@ -432,13 +465,13 @@ TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) { EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer))); } +#endif // The following group tests that two PeerConnections can successfully exchange // an offer/answer when DTLS is on and that they will refuse any offer/answer // applied locally/remotely if it does not include a DTLS fingerprint. TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto callee = CreatePeerConnectionWithAudioVideo(config); @@ -453,7 +486,6 @@ TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsOn) { TEST_P(PeerConnectionCryptoTest, FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto offer = caller->CreateOffer(); @@ -464,7 +496,6 @@ TEST_P(PeerConnectionCryptoTest, TEST_P(PeerConnectionCryptoTest, FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto callee = CreatePeerConnectionWithAudioVideo(config); @@ -476,7 +507,6 @@ TEST_P(PeerConnectionCryptoTest, TEST_P(PeerConnectionCryptoTest, FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto callee = CreatePeerConnectionWithAudioVideo(config); @@ -487,7 +517,6 @@ TEST_P(PeerConnectionCryptoTest, TEST_P(PeerConnectionCryptoTest, FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto caller = CreatePeerConnectionWithAudioVideo(config); auto callee = CreatePeerConnectionWithAudioVideo(config); @@ -498,6 +527,7 @@ TEST_P(PeerConnectionCryptoTest, EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer))); } +#if defined(WEBRTC_FUCHSIA) // Test that an offer/answer can be exchanged when encryption is disabled. TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) { PeerConnectionFactoryInterface::Options options; @@ -517,19 +547,18 @@ TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenNoEncryption) { ASSERT_TRUE(answer); ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer))); } +#endif // Tests that a DTLS call can be established when the certificate is specified // in the PeerConnection config and no certificate generator is specified. TEST_P(PeerConnectionCryptoTest, ExchangeOfferAnswerWhenDtlsCertificateInConfig) { RTCConfiguration caller_config; - caller_config.enable_dtls_srtp.emplace(true); caller_config.certificates.push_back( FakeRTCCertificateGenerator::GenerateCertificate()); auto caller = CreatePeerConnectionWithAudioVideo(caller_config); RTCConfiguration callee_config; - callee_config.enable_dtls_srtp.emplace(true); callee_config.certificates.push_back( FakeRTCCertificateGenerator::GenerateCertificate()); auto callee = CreatePeerConnectionWithAudioVideo(callee_config); @@ -600,7 +629,6 @@ class PeerConnectionCryptoDtlsCertGenTest TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) { RTCConfiguration config; - config.enable_dtls_srtp.emplace(true); auto owned_fake_certificate_generator = std::make_unique(); auto* fake_certificate_generator = owned_fake_certificate_generator.get(); @@ -634,10 +662,10 @@ TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) { rtc::make_ref_counted(); observers.push_back(observer); if (sdp_type_ == SdpType::kOffer) { - pc->pc()->CreateOffer(observer, + pc->pc()->CreateOffer(observer.get(), PeerConnectionInterface::RTCOfferAnswerOptions()); } else { - pc->pc()->CreateAnswer(observer, + pc->pc()->CreateAnswer(observer.get(), PeerConnectionInterface::RTCOfferAnswerOptions()); } } @@ -654,7 +682,7 @@ TEST_P(PeerConnectionCryptoDtlsCertGenTest, TestCertificateGeneration) { INSTANTIATE_TEST_SUITE_P( PeerConnectionCryptoTest, PeerConnectionCryptoDtlsCertGenTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(SdpType::kOffer, SdpType::kAnswer), Values(CertGenTime::kBefore, CertGenTime::kDuring), Values(CertGenResult::kSucceed, CertGenResult::kFail), @@ -724,7 +752,6 @@ TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) { auto caller = CreatePeerConnectionWithAudioVideo(); RTCConfiguration callee_config; - callee_config.enable_dtls_srtp.emplace(true); callee_config.certificates.push_back(callee_certificate); auto callee = CreatePeerConnectionWithAudioVideo(callee_config); @@ -760,7 +787,7 @@ TEST_P(PeerConnectionCryptoTest, SessionErrorIfFingerprintInvalid) { INSTANTIATE_TEST_SUITE_P(PeerConnectionCryptoTest, PeerConnectionCryptoTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); } // namespace webrtc diff --git a/pc/peer_connection_data_channel_unittest.cc b/pc/peer_connection_data_channel_unittest.cc index 5a6377b00d..3bb2088866 100644 --- a/pc/peer_connection_data_channel_unittest.cc +++ b/pc/peer_connection_data_channel_unittest.cc @@ -20,24 +20,24 @@ #include "api/media_types.h" #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" +#include "api/sctp_transport_interface.h" #include "api/task_queue/default_task_queue_factory.h" -#include "media/base/codec.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/sctp_transport_factory_interface.h" #include "media/base/fake_media_engine.h" -#include "media/base/media_constants.h" #include "media/base/media_engine.h" -#include "media/sctp/sctp_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port_allocator.h" #include "pc/media_session.h" #include "pc/peer_connection.h" -#include "pc/peer_connection_factory.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" +#include "pc/sctp_transport.h" #include "pc/sdp_utils.h" #include "pc/session_description.h" #include "pc/test/mock_peer_connection_observers.h" #include "rtc_base/checks.h" -#include "rtc_base/ref_counted_object.h" +#include "rtc_base/logging.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/thread.h" #include "test/gmock.h" @@ -138,15 +138,15 @@ class PeerConnectionDataChannelBaseTest : public ::testing::Test { auto observer = std::make_unique(); RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - auto pc = pc_factory->CreatePeerConnection(modified_config, nullptr, - nullptr, observer.get()); - if (!pc) { + auto result = pc_factory->CreatePeerConnectionOrError( + modified_config, PeerConnectionDependencies(observer.get())); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); + observer->SetPeerConnectionInterface(result.value().get()); auto wrapper = std::make_unique( - pc_factory, pc, std::move(observer)); + pc_factory, result.MoveValue(), std::move(observer)); wrapper->set_sctp_transport_factory(fake_sctp_transport_factory); return wrapper; } @@ -159,7 +159,7 @@ class PeerConnectionDataChannelBaseTest : public ::testing::Test { if (!wrapper) { return nullptr; } - EXPECT_TRUE(wrapper->pc()->CreateDataChannel("dc", nullptr)); + EXPECT_TRUE(wrapper->pc()->CreateDataChannelOrError("dc", nullptr).ok()); return wrapper; } @@ -222,7 +222,7 @@ TEST_P(PeerConnectionDataChannelTest, SctpContentAndTransportNameSetCorrectly) { // transport. caller->AddAudioTrack("a"); caller->AddVideoTrack("v"); - caller->pc()->CreateDataChannel("dc", nullptr); + caller->pc()->CreateDataChannelOrError("dc", nullptr); auto offer = caller->CreateOffer(); const auto& offer_contents = offer->description()->contents(); @@ -331,7 +331,7 @@ TEST_P(PeerConnectionDataChannelTest, ObsoleteSdpSyntaxIfSet) { INSTANTIATE_TEST_SUITE_P(PeerConnectionDataChannelTest, PeerConnectionDataChannelTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); } // namespace webrtc diff --git a/pc/peer_connection_end_to_end_unittest.cc b/pc/peer_connection_end_to_end_unittest.cc index 3a6402703f..08e3cb1848 100644 --- a/pc/peer_connection_end_to_end_unittest.cc +++ b/pc/peer_connection_end_to_end_unittest.cc @@ -8,20 +8,46 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + +#include +#include #include +#include +#include +#include +#include #include "absl/strings/match.h" +#include "absl/types/optional.h" #include "api/audio_codecs/L16/audio_decoder_L16.h" #include "api/audio_codecs/L16/audio_encoder_L16.h" #include "api/audio_codecs/audio_codec_pair_id.h" +#include "api/audio_codecs/audio_decoder.h" +#include "api/audio_codecs/audio_decoder_factory.h" #include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder.h" +#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/audio_format.h" #include "api/audio_codecs/opus_audio_decoder_factory.h" #include "api/audio_codecs/opus_audio_encoder_factory.h" +#include "api/audio_options.h" +#include "api/data_channel_interface.h" +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" #include "media/sctp/sctp_transport_internal.h" +#include "rtc_base/checks.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" -#include "rtc_base/logging.h" #include "rtc_base/physical_socket_server.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" +#include "test/gmock.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -370,6 +396,7 @@ TEST_P(PeerConnectionEndToEndTest, Call) { WaitForCallEstablished(); } +#if defined(IS_FUCHSIA) TEST_P(PeerConnectionEndToEndTest, CallWithSdesKeyNegotiation) { config_.enable_dtls_srtp = false; CreatePcs(webrtc::CreateOpusAudioEncoderFactory(), @@ -378,6 +405,7 @@ TEST_P(PeerConnectionEndToEndTest, CallWithSdesKeyNegotiation) { Negotiate(); WaitForCallEstablished(); } +#endif TEST_P(PeerConnectionEndToEndTest, CallWithCustomCodec) { class IdLoggingAudioEncoderFactory : public webrtc::AudioEncoderFactory { @@ -481,14 +509,16 @@ TEST_P(PeerConnectionEndToEndTest, CreateDataChannelBeforeNegotiate) { Negotiate(); WaitForConnection(); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0); - WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0); - TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[0]); - TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]); + TestDataChannelSendAndReceive(caller_dc.get(), + callee_signaled_data_channels_[0].get()); + TestDataChannelSendAndReceive(callee_dc.get(), + caller_signaled_data_channels_[0].get()); - CloseDataChannels(caller_dc, callee_signaled_data_channels_, 0); - CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0); + CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 0); + CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0); } // Verifies that a DataChannel created after the negotiation can transition to @@ -506,7 +536,7 @@ TEST_P(PeerConnectionEndToEndTest, CreateDataChannelAfterNegotiate) { WaitForConnection(); // Wait for the data channel created pre-negotiation to be opened. - WaitForDataChannelsToOpen(dummy, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(dummy.get(), callee_signaled_data_channels_, 0); // Create new DataChannels after the negotiation and verify their states. rtc::scoped_refptr caller_dc( @@ -514,14 +544,16 @@ TEST_P(PeerConnectionEndToEndTest, CreateDataChannelAfterNegotiate) { rtc::scoped_refptr callee_dc( callee_->CreateDataChannel("hello", init)); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1); - WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0); - TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1]); - TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0]); + TestDataChannelSendAndReceive(caller_dc.get(), + callee_signaled_data_channels_[1].get()); + TestDataChannelSendAndReceive(callee_dc.get(), + caller_signaled_data_channels_[0].get()); - CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1); - CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0); + CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1); + CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0); } // Verifies that a DataChannel created can transfer large messages. @@ -538,7 +570,7 @@ TEST_P(PeerConnectionEndToEndTest, CreateDataChannelLargeTransfer) { WaitForConnection(); // Wait for the data channel created pre-negotiation to be opened. - WaitForDataChannelsToOpen(dummy, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(dummy.get(), callee_signaled_data_channels_, 0); // Create new DataChannels after the negotiation and verify their states. rtc::scoped_refptr caller_dc( @@ -546,16 +578,16 @@ TEST_P(PeerConnectionEndToEndTest, CreateDataChannelLargeTransfer) { rtc::scoped_refptr callee_dc( callee_->CreateDataChannel("hello", init)); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1); - WaitForDataChannelsToOpen(callee_dc, caller_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(callee_dc.get(), caller_signaled_data_channels_, 0); - TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1], - 256 * 1024); - TestDataChannelSendAndReceive(callee_dc, caller_signaled_data_channels_[0], - 256 * 1024); + TestDataChannelSendAndReceive( + caller_dc.get(), callee_signaled_data_channels_[1].get(), 256 * 1024); + TestDataChannelSendAndReceive( + callee_dc.get(), caller_signaled_data_channels_[0].get(), 256 * 1024); - CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1); - CloseDataChannels(callee_dc, caller_signaled_data_channels_, 0); + CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1); + CloseDataChannels(callee_dc.get(), caller_signaled_data_channels_, 0); } // Verifies that DataChannel IDs are even/odd based on the DTLS roles. @@ -600,14 +632,18 @@ TEST_P(PeerConnectionEndToEndTest, Negotiate(); WaitForConnection(); - WaitForDataChannelsToOpen(caller_dc_1, callee_signaled_data_channels_, 0); - WaitForDataChannelsToOpen(caller_dc_2, callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(caller_dc_1.get(), callee_signaled_data_channels_, + 0); + WaitForDataChannelsToOpen(caller_dc_2.get(), callee_signaled_data_channels_, + 1); std::unique_ptr dc_1_observer( - new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[0])); + new webrtc::MockDataChannelObserver( + callee_signaled_data_channels_[0].get())); std::unique_ptr dc_2_observer( - new webrtc::MockDataChannelObserver(callee_signaled_data_channels_[1])); + new webrtc::MockDataChannelObserver( + callee_signaled_data_channels_[1].get())); const std::string message_1 = "hello 1"; const std::string message_2 = "hello 2"; @@ -638,7 +674,7 @@ TEST_P(PeerConnectionEndToEndTest, Negotiate(); WaitForConnection(); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0); int first_channel_id = caller_dc->id(); // Wait for the local side to say it's closed, but not the remote side. // Previously, the channel on which Close is called reported being closed @@ -648,13 +684,14 @@ TEST_P(PeerConnectionEndToEndTest, // Create a new channel and ensure it works after closing the previous one. caller_dc = caller_->CreateDataChannel("data2", init); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1); // Since the second channel was created after the first finished closing, it // should be able to re-use the first one's ID. EXPECT_EQ(first_channel_id, caller_dc->id()); - TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1]); + TestDataChannelSendAndReceive(caller_dc.get(), + callee_signaled_data_channels_[1].get()); - CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1); + CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1); } // Similar to the above test, but don't wait for the first channel to finish @@ -671,20 +708,21 @@ TEST_P(PeerConnectionEndToEndTest, Negotiate(); WaitForConnection(); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0); int first_channel_id = caller_dc->id(); caller_dc->Close(); // Immediately create a new channel, before waiting for the previous one to // transition to "closed". caller_dc = caller_->CreateDataChannel("data2", init); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 1); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 1); // Since the second channel was created while the first was still closing, // it should have been assigned a different ID. EXPECT_NE(first_channel_id, caller_dc->id()); - TestDataChannelSendAndReceive(caller_dc, callee_signaled_data_channels_[1]); + TestDataChannelSendAndReceive(caller_dc.get(), + callee_signaled_data_channels_[1].get()); - CloseDataChannels(caller_dc, callee_signaled_data_channels_, 1); + CloseDataChannels(caller_dc.get(), callee_signaled_data_channels_, 1); } // This tests that if a data channel is closed remotely while not referenced @@ -702,7 +740,7 @@ TEST_P(PeerConnectionEndToEndTest, CloseDataChannelRemotelyWhileNotReferenced) { Negotiate(); WaitForConnection(); - WaitForDataChannelsToOpen(caller_dc, callee_signaled_data_channels_, 0); + WaitForDataChannelsToOpen(caller_dc.get(), callee_signaled_data_channels_, 0); // This removes the reference to the remote data channel that we hold. callee_signaled_data_channels_.clear(); caller_dc->Close(); @@ -756,5 +794,5 @@ TEST_P(PeerConnectionEndToEndTest, CanRestartIce) { INSTANTIATE_TEST_SUITE_P(PeerConnectionEndToEndTest, PeerConnectionEndToEndTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); diff --git a/pc/peer_connection_factory.cc b/pc/peer_connection_factory.cc index db2f468a01..6e6a72ced8 100644 --- a/pc/peer_connection_factory.cc +++ b/pc/peer_connection_factory.cc @@ -10,7 +10,7 @@ #include "pc/peer_connection_factory.h" -#include +#include #include #include "absl/strings/match.h" @@ -33,6 +33,7 @@ #include "p2p/base/port_allocator.h" #include "p2p/client/basic_port_allocator.h" #include "pc/audio_track.h" +#include "pc/channel_manager.h" #include "pc/local_audio_source.h" #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" @@ -243,9 +244,12 @@ PeerConnectionFactory::CreatePeerConnectionOrError( worker_thread()->Invoke>( RTC_FROM_HERE, [this] { return CreateRtcEventLog_w(); }); + const FieldTrialsView* trials = + dependencies.trials ? dependencies.trials.get() : &field_trials(); std::unique_ptr call = worker_thread()->Invoke>( - RTC_FROM_HERE, - [this, &event_log] { return CreateCall_w(event_log.get()); }); + RTC_FROM_HERE, [this, &event_log, trials] { + return CreateCall_w(event_log.get(), *trials); + }); auto result = PeerConnection::Create(context_, options_, std::move(event_log), std::move(call), configuration, @@ -276,8 +280,9 @@ rtc::scoped_refptr PeerConnectionFactory::CreateVideoTrack( const std::string& id, VideoTrackSourceInterface* source) { RTC_DCHECK(signaling_thread()->IsCurrent()); - rtc::scoped_refptr track( - VideoTrack::Create(id, source, worker_thread())); + rtc::scoped_refptr track = VideoTrack::Create( + id, rtc::scoped_refptr(source), + worker_thread()); return VideoTrackProxy::Create(signaling_thread(), worker_thread(), track); } @@ -285,15 +290,11 @@ rtc::scoped_refptr PeerConnectionFactory::CreateAudioTrack( const std::string& id, AudioSourceInterface* source) { RTC_DCHECK(signaling_thread()->IsCurrent()); - rtc::scoped_refptr track( - AudioTrack::Create(id, rtc::scoped_refptr(source))); + rtc::scoped_refptr track = + AudioTrack::Create(id, rtc::scoped_refptr(source)); return AudioTrackProxy::Create(signaling_thread(), track); } -cricket::ChannelManager* PeerConnectionFactory::channel_manager() { - return context_->channel_manager(); -} - std::unique_ptr PeerConnectionFactory::CreateRtcEventLog_w() { RTC_DCHECK_RUN_ON(worker_thread()); @@ -306,7 +307,8 @@ std::unique_ptr PeerConnectionFactory::CreateRtcEventLog_w() { } std::unique_ptr PeerConnectionFactory::CreateCall_w( - RtcEventLog* event_log) { + RtcEventLog* event_log, + const FieldTrialsView& field_trials) { RTC_DCHECK_RUN_ON(worker_thread()); webrtc::Call::Config call_config(event_log, network_thread()); @@ -323,7 +325,7 @@ std::unique_ptr PeerConnectionFactory::CreateCall_w( FieldTrialParameter max_bandwidth("max", DataRate::KilobitsPerSec(2000)); ParseFieldTrial({&min_bandwidth, &start_bandwidth, &max_bandwidth}, - trials().Lookup("WebRTC-PcFactoryDefaultBitrates")); + field_trials.Lookup("WebRTC-PcFactoryDefaultBitrates")); call_config.bitrate_config.min_bitrate_bps = rtc::saturated_cast(min_bandwidth->bps()); @@ -346,7 +348,7 @@ std::unique_ptr PeerConnectionFactory::CreateCall_w( RTC_LOG(LS_INFO) << "Using default network controller factory"; } - call_config.trials = &trials(); + call_config.trials = &field_trials; call_config.rtp_transport_controller_send_factory = transport_controller_send_factory_.get(); call_config.metronome = metronome_.get(); @@ -355,7 +357,7 @@ std::unique_ptr PeerConnectionFactory::CreateCall_w( } bool PeerConnectionFactory::IsTrialEnabled(absl::string_view key) const { - return absl::StartsWith(trials().Lookup(key), "Enabled"); + return absl::StartsWith(field_trials().Lookup(key), "Enabled"); } } // namespace webrtc diff --git a/pc/peer_connection_factory.h b/pc/peer_connection_factory.h index c1599f4885..ff3d515964 100644 --- a/pc/peer_connection_factory.h +++ b/pc/peer_connection_factory.h @@ -21,8 +21,10 @@ #include "absl/strings/string_view.h" #include "api/audio_options.h" #include "api/fec_controller.h" +#include "api/field_trials_view.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/metronome/metronome.h" #include "api/neteq/neteq_factory.h" #include "api/network_state_predictor.h" #include "api/peer_connection_interface.h" @@ -35,17 +37,19 @@ #include "api/task_queue/task_queue_factory.h" #include "api/transport/network_control.h" #include "api/transport/sctp_transport_factory_interface.h" -#include "api/transport/webrtc_key_value_config.h" #include "call/call.h" #include "call/rtp_transport_controller_send_factory_interface.h" #include "p2p/base/port_allocator.h" -#include "pc/channel_manager.h" #include "pc/connection_context.h" #include "rtc_base/checks.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" +namespace cricket { +class ChannelManager; +} + namespace rtc { class BasicNetworkManager; class BasicPacketSocketFactory; @@ -99,8 +103,6 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { return context_->sctp_transport_factory(); } - virtual cricket::ChannelManager* channel_manager(); - rtc::Thread* signaling_thread() const { // This method can be called on a different thread when the factory is // created in CreatePeerConnectionFactory(). @@ -114,7 +116,9 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { return options_; } - const WebRtcKeyValueConfig& trials() const { return context_->trials(); } + const FieldTrialsView& field_trials() const { + return context_->field_trials(); + } protected: // Constructor used by the static Create() method. Modifies the dependencies. @@ -132,12 +136,17 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface { rtc::Thread* network_thread() const { return context_->network_thread(); } bool IsTrialEnabled(absl::string_view key) const; + + cricket::ChannelManager* channel_manager() { + return context_->channel_manager(); + } const cricket::ChannelManager* channel_manager() const { return context_->channel_manager(); } std::unique_ptr CreateRtcEventLog_w(); - std::unique_ptr CreateCall_w(RtcEventLog* event_log); + std::unique_ptr CreateCall_w(RtcEventLog* event_log, + const FieldTrialsView& field_trials); rtc::scoped_refptr context_; PeerConnectionFactoryInterface::Options options_ diff --git a/pc/peer_connection_factory_unittest.cc b/pc/peer_connection_factory_unittest.cc index c12b563e27..442a33135c 100644 --- a/pc/peer_connection_factory_unittest.cc +++ b/pc/peer_connection_factory_unittest.cc @@ -10,16 +10,10 @@ #include "pc/peer_connection_factory.h" -#include - -#include -#include #include #include #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" @@ -28,17 +22,18 @@ #include "api/media_stream_interface.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/fake_frame_source.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" #include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/fake_video_track_source.h" +#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" +#include "rtc_base/time_utils.h" #include "test/gtest.h" #ifdef WEBRTC_ANDROID @@ -194,10 +189,12 @@ TEST(PeerConnectionFactoryTestInternal, DISABLED_CreatePCUsingInternalModules) { std::unique_ptr cert_generator( new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc(factory->CreatePeerConnection( - config, nullptr, std::move(cert_generator), &observer)); + webrtc::PeerConnectionDependencies pc_dependencies(&observer); + pc_dependencies.cert_generator = std::move(cert_generator); + auto result = + factory->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); - EXPECT_TRUE(pc.get() != nullptr); + EXPECT_TRUE(result.ok()); } TEST_F(PeerConnectionFactoryTest, CheckRtpSenderAudioCapabilities) { @@ -282,12 +279,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); cricket::ServerAddresses stun_servers; rtc::SocketAddress stun1("stun.l.google.com", 19302); stun_servers.insert(stun1); @@ -314,12 +312,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServersUrls) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); cricket::ServerAddresses stun_servers; rtc::SocketAddress stun1("stun.l.google.com", 19302); stun_servers.insert(stun1); @@ -344,12 +343,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingNoUsernameInUri) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); std::vector turn_servers; cricket::RelayServerConfig turn("test.com", 1234, kTurnUsername, kTurnPassword, cricket::PROTO_UDP); @@ -367,12 +367,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); std::vector turn_servers; cricket::RelayServerConfig turn("hello.com", kDefaultStunPort, kTurnUsername, kTurnPassword, cricket::PROTO_TCP); @@ -396,12 +397,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); std::vector turn_servers; cricket::RelayServerConfig turn1("hello.com", kDefaultStunTlsPort, kTurnUsername, kTurnPassword, @@ -434,12 +436,13 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { ice_server.username = kTurnUsername; ice_server.password = kTurnPassword; config.servers.push_back(ice_server); - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - rtc::scoped_refptr pc( - factory_->CreatePeerConnection(config, std::move(port_allocator_), - std::move(cert_generator), &observer_)); - ASSERT_TRUE(pc.get() != NULL); + webrtc::PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = + std::make_unique(); + pc_dependencies.allocator = std::move(port_allocator_); + auto result = + factory_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); cricket::ServerAddresses stun_servers; rtc::SocketAddress stun1("1.2.3.4", 1234); stun_servers.insert(stun1); @@ -469,9 +472,9 @@ TEST_F(PeerConnectionFactoryTest, LocalRendering) { ASSERT_TRUE(source.get() != NULL); rtc::scoped_refptr track( - factory_->CreateVideoTrack("testlabel", source)); + factory_->CreateVideoTrack("testlabel", source.get())); ASSERT_TRUE(track.get() != NULL); - FakeVideoTrackRenderer local_renderer(track); + FakeVideoTrackRenderer local_renderer(track.get()); EXPECT_EQ(0, local_renderer.num_rendered_frames()); source->InjectFrame(frame_source.GetFrame()); diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc index 8bf6c7ab44..ba78dd85fc 100644 --- a/pc/peer_connection_header_extension_unittest.cc +++ b/pc/peer_connection_header_extension_unittest.cc @@ -9,17 +9,38 @@ */ #include +#include #include - +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/call/call_factory_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" #include "p2p/base/fake_port_allocator.h" -#include "pc/media_session.h" +#include "p2p/base/port_allocator.h" #include "pc/peer_connection_wrapper.h" -#include "rtc_base/gunit.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread.h" #include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { @@ -80,11 +101,14 @@ class PeerConnectionHeaderExtensionTest PeerConnectionInterface::RTCConfiguration config; if (semantics) config.sdp_semantics = *semantics; - auto pc = pc_factory->CreatePeerConnection( - config, std::move(fake_port_allocator), nullptr, observer.get()); - observer->SetPeerConnectionInterface(pc.get()); - return std::make_unique(pc_factory, pc, - std::move(observer)); + PeerConnectionDependencies pc_dependencies(observer.get()); + pc_dependencies.allocator = std::move(fake_port_allocator); + auto result = pc_factory->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + EXPECT_TRUE(result.ok()); + observer->SetPeerConnectionInterface(result.value().get()); + return std::make_unique( + pc_factory, result.MoveValue(), std::move(observer)); } std::vector extensions_; @@ -199,7 +223,7 @@ TEST_P(PeerConnectionHeaderExtensionTest, NegotiatedExtensionsAreAccessible) { INSTANTIATE_TEST_SUITE_P( , PeerConnectionHeaderExtensionTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(cricket::MediaType::MEDIA_TYPE_AUDIO, cricket::MediaType::MEDIA_TYPE_VIDEO)), [](const testing::TestParamInfo< @@ -208,7 +232,8 @@ INSTANTIATE_TEST_SUITE_P( SdpSemantics semantics; std::tie(media_type, semantics) = info.param; return (rtc::StringBuilder("With") - << (semantics == SdpSemantics::kPlanB ? "PlanB" : "UnifiedPlan") + << (semantics == SdpSemantics::kPlanB_DEPRECATED ? "PlanB" + : "UnifiedPlan") << "And" << (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ? "Voice" : "Video") diff --git a/pc/peer_connection_histogram_unittest.cc b/pc/peer_connection_histogram_unittest.cc index 27bae854d6..4a3c6794c4 100644 --- a/pc/peer_connection_histogram_unittest.cc +++ b/pc/peer_connection_histogram_unittest.cc @@ -15,6 +15,7 @@ #include #include "absl/types/optional.h" +#include "api/async_resolver_factory.h" #include "api/call/call_factory_interface.h" #include "api/jsep.h" #include "api/jsep_session_description.h" @@ -22,7 +23,9 @@ #include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" #include "p2p/base/mock_async_resolver.h" #include "p2p/base/port_allocator.h" #include "p2p/client/basic_port_allocator.h" @@ -39,13 +42,14 @@ #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" #include "rtc_base/gunit.h" +#include "rtc_base/mdns_responder_interface.h" #include "rtc_base/ref_counted_object.h" -#include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { @@ -71,10 +75,10 @@ int MakeUsageFingerprint(std::set events) { } class PeerConnectionFactoryForUsageHistogramTest - : public rtc::RefCountedObject { + : public PeerConnectionFactory { public: PeerConnectionFactoryForUsageHistogramTest() - : rtc::RefCountedObject([] { + : PeerConnectionFactory([] { PeerConnectionFactoryDependencies dependencies; dependencies.network_thread = rtc::Thread::Current(); dependencies.worker_thread = rtc::Thread::Current(); @@ -103,7 +107,7 @@ class ObserverForUsageHistogramTest : public MockPeerConnectionObserver { candidate_target_ = other; } - bool HaveDataChannel() { return last_datachannel_; } + bool HaveDataChannel() { return last_datachannel_ != nullptr; } absl::optional interesting_usage_detected() { return interesting_usage_detected_; @@ -326,8 +330,8 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { const RTCConfiguration& config, const PeerConnectionFactoryInterface::Options factory_options, PeerConnectionDependencies deps) { - rtc::scoped_refptr pc_factory( - new PeerConnectionFactoryForUsageHistogramTest()); + auto pc_factory = + rtc::make_ref_counted(); pc_factory->SetOptions(factory_options); // If no allocator is provided, one will be created using a network manager @@ -342,14 +346,15 @@ class PeerConnectionUsageHistogramTest : public ::testing::Test { auto observer = std::make_unique(); deps.observer = observer.get(); - auto pc = pc_factory->CreatePeerConnection(config, std::move(deps)); - if (!pc) { + auto result = + pc_factory->CreatePeerConnectionOrError(config, std::move(deps)); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); + observer->SetPeerConnectionInterface(result.value().get()); auto wrapper = std::make_unique( - pc_factory, pc, std::move(observer)); + pc_factory, result.MoveValue(), std::move(observer)); return wrapper; } diff --git a/pc/peer_connection_ice_unittest.cc b/pc/peer_connection_ice_unittest.cc index c04ff8e204..56768377b0 100644 --- a/pc/peer_connection_ice_unittest.cc +++ b/pc/peer_connection_ice_unittest.cc @@ -8,15 +8,52 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" +#include "api/candidate.h" +#include "api/ice_transport_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/fake_port_allocator.h" -#include "p2p/base/test_stun_server.h" +#include "p2p/base/ice_transport_internal.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_info.h" #include "p2p/client/basic_port_allocator.h" +#include "pc/channel_interface.h" +#include "pc/dtls_transport.h" #include "pc/media_session.h" #include "pc/peer_connection.h" #include "pc/peer_connection_wrapper.h" +#include "pc/rtp_transceiver.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "rtc_base/checks.h" +#include "rtc_base/ip_address.h" +#include "rtc_base/logging.h" +#include "rtc_base/net_helper.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -130,15 +167,17 @@ class PeerConnectionIceBaseTest : public ::testing::Test { modified_config.sdp_semantics = sdp_semantics_; auto observer = std::make_unique(); auto port_allocator_copy = port_allocator.get(); - auto pc = pc_factory_->CreatePeerConnection( - modified_config, std::move(port_allocator), nullptr, observer.get()); - if (!pc) { + PeerConnectionDependencies pc_dependencies(observer.get()); + pc_dependencies.allocator = std::move(port_allocator); + auto result = pc_factory_->CreatePeerConnectionOrError( + modified_config, std::move(pc_dependencies)); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); + observer->SetPeerConnectionInterface(result.value().get()); auto wrapper = std::make_unique( - pc_factory_, pc, std::move(observer)); + pc_factory_, result.MoveValue(), std::move(observer)); wrapper->set_network(fake_network); wrapper->port_allocator_ = port_allocator_copy; return wrapper; @@ -770,7 +809,7 @@ TEST_P(PeerConnectionIceTest, // Chain an operation that will block AddIceCandidate() from executing. auto answer_observer = rtc::make_ref_counted(); - callee->pc()->CreateAnswer(answer_observer, RTCOfferAnswerOptions()); + callee->pc()->CreateAnswer(answer_observer.get(), RTCOfferAnswerOptions()); auto jsep_candidate = callee->CreateJsepCandidateForFirstTransport(&candidate); @@ -818,7 +857,7 @@ TEST_P(PeerConnectionIceTest, // Chain an operation that will block AddIceCandidate() from executing. auto answer_observer = rtc::make_ref_counted(); - callee->pc()->CreateAnswer(answer_observer, RTCOfferAnswerOptions()); + callee->pc()->CreateAnswer(answer_observer.get(), RTCOfferAnswerOptions()); auto jsep_candidate = callee->CreateJsepCandidateForFirstTransport(&candidate); @@ -1165,7 +1204,7 @@ TEST_F(PeerConnectionIceTestUnifiedPlan, class PeerConnectionIceTestPlanB : public PeerConnectionIceBaseTest { protected: PeerConnectionIceTestPlanB() - : PeerConnectionIceBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionIceBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; TEST_F(PeerConnectionIceTestPlanB, @@ -1264,7 +1303,7 @@ TEST_P(PeerConnectionIceUfragPwdAnswerTest, TestIncludedInAnswer) { INSTANTIATE_TEST_SUITE_P( PeerConnectionIceTest, PeerConnectionIceUfragPwdAnswerTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_pair(true, true), // Both changed. std::make_pair(true, false), // Only ufrag changed. std::make_pair(false, true)))); // Only pwd changed. @@ -1361,7 +1400,7 @@ TEST_P(PeerConnectionIceTest, INSTANTIATE_TEST_SUITE_P(PeerConnectionIceTest, PeerConnectionIceTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); class PeerConnectionIceConfigTest : public ::testing::Test { @@ -1378,12 +1417,12 @@ class PeerConnectionIceConfigTest : public ::testing::Test { std::unique_ptr port_allocator( new cricket::FakePortAllocator(rtc::Thread::Current(), nullptr)); port_allocator_ = port_allocator.get(); - rtc::scoped_refptr pc( - pc_factory_->CreatePeerConnection(config, std::move(port_allocator), - nullptr /* cert_generator */, - &observer_)); - EXPECT_TRUE(pc.get()); - pc_ = std::move(pc); + PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.allocator = std::move(port_allocator); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + EXPECT_TRUE(result.ok()); + pc_ = result.MoveValue(); } rtc::scoped_refptr pc_factory_ = nullptr; diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc index f4e25ca7ea..940d7fdc74 100644 --- a/pc/peer_connection_integrationtest.cc +++ b/pc/peer_connection_integrationtest.cc @@ -8,6 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ +// Integration tests for PeerConnection. +// These tests exercise a full stack over a simulated network. +// +// NOTE: If your test takes a while (guideline: more than 5 seconds), +// do NOT add it here, but instead add it to the file +// slow_peer_connection_integrationtest.cc + #include #include @@ -18,6 +25,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/async_resolver_factory.h" #include "api/candidate.h" @@ -54,7 +62,6 @@ #include "p2p/base/port.h" #include "p2p/base/port_allocator.h" #include "p2p/base/port_interface.h" -#include "p2p/base/stun_server.h" #include "p2p/base/test_stun_server.h" #include "p2p/base/test_turn_customizer.h" #include "p2p/base/test_turn_server.h" @@ -86,6 +93,8 @@ #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" +#include "test/gmock.h" +#include "test/gtest.h" namespace webrtc { @@ -93,10 +102,12 @@ namespace { class PeerConnectionIntegrationTest : public PeerConnectionIntegrationBaseTest, - public ::testing::WithParamInterface { + public ::testing::WithParamInterface< + std::tuple> { protected: PeerConnectionIntegrationTest() - : PeerConnectionIntegrationBaseTest(GetParam()) {} + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam()), + std::get<1>(GetParam())) {} }; // Fake clock must be set before threads are started to prevent race on @@ -127,7 +138,7 @@ class PeerConnectionIntegrationTestPlanB : public PeerConnectionIntegrationBaseTest { protected: PeerConnectionIntegrationTestPlanB() - : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; class PeerConnectionIntegrationTestUnifiedPlan @@ -264,6 +275,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithDtls) { webrtc::kEnumCounterKeyProtocolSdes)); } +#if defined(WEBRTC_FUCHSIA) // Uses SDES instead of DTLS for key agreement. TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { PeerConnectionInterface::RTCConfiguration sdes_config; @@ -287,6 +299,7 @@ TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { 0, webrtc::metrics::NumEvents("WebRTC.PeerConnection.KeyProtocol", webrtc::kEnumCounterKeyProtocolDtls)); } +#endif // Basic end-to-end test specifying the `enable_encrypted_rtp_header_extensions` // option to offer encrypted versions of all header extensions alongside the @@ -528,7 +541,7 @@ TEST_P(PeerConnectionIntegrationTest, AudioToVideoUpgrade) { // send/receive video on the callee side. caller()->AddAudioVideoTracks(); callee()->AddAudioTrack(); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_video = 0; callee()->SetOfferAnswerOptions(options); @@ -558,7 +571,7 @@ TEST_P(PeerConnectionIntegrationTest, AudioToVideoUpgrade) { // Now negotiate with video and ensure negotiation succeeds, with video // frames and additional audio frames being received. callee()->AddVideoTrack(); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_video = 1; callee()->SetOfferAnswerOptions(options); @@ -571,7 +584,8 @@ TEST_P(PeerConnectionIntegrationTest, AudioToVideoUpgrade) { ASSERT_EQ(2U, transceivers.size()); ASSERT_EQ(cricket::MEDIA_TYPE_VIDEO, transceivers[1]->receiver()->media_type()); - transceivers[1]->sender()->SetTrack(caller()->CreateLocalVideoTrack()); + transceivers[1]->sender()->SetTrack( + caller()->CreateLocalVideoTrack().get()); transceivers[1]->SetDirectionWithError( RtpTransceiverDirection::kSendRecv); }); @@ -607,65 +621,6 @@ TEST_P(PeerConnectionIntegrationTest, AddAudioToVideoOnlyCall) { ASSERT_TRUE(ExpectNewFrames(media_expectations)); } -// This test sets up a call that's transferred to a new caller with a different -// DTLS fingerprint. -TEST_P(PeerConnectionIntegrationTest, CallTransferredForCallee) { - ASSERT_TRUE(CreatePeerConnectionWrappers()); - ConnectFakeSignaling(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - - // Keep the original peer around which will still send packets to the - // receiving client. These SRTP packets will be dropped. - std::unique_ptr original_peer( - SetCallerPcWrapperAndReturnCurrent( - CreatePeerConnectionWrapperWithAlternateKey().release())); - // TODO(deadbeef): Why do we call Close here? That goes against the comment - // directly above. - original_peer->pc()->Close(); - - ConnectFakeSignaling(); - caller()->AddAudioVideoTracks(); - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Wait for some additional frames to be transmitted end-to-end. - MediaExpectations media_expectations; - media_expectations.ExpectBidirectionalAudioAndVideo(); - ASSERT_TRUE(ExpectNewFrames(media_expectations)); -} - -// This test sets up a call that's transferred to a new callee with a different -// DTLS fingerprint. -TEST_P(PeerConnectionIntegrationTest, CallTransferredForCaller) { - ASSERT_TRUE(CreatePeerConnectionWrappers()); - ConnectFakeSignaling(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - - // Keep the original peer around which will still send packets to the - // receiving client. These SRTP packets will be dropped. - std::unique_ptr original_peer( - SetCalleePcWrapperAndReturnCurrent( - CreatePeerConnectionWrapperWithAlternateKey().release())); - // TODO(deadbeef): Why do we call Close here? That goes against the comment - // directly above. - original_peer->pc()->Close(); - - ConnectFakeSignaling(); - callee()->AddAudioVideoTracks(); - caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions()); - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Wait for some additional frames to be transmitted end-to-end. - MediaExpectations media_expectations; - media_expectations.ExpectBidirectionalAudioAndVideo(); - ASSERT_TRUE(ExpectNewFrames(media_expectations)); -} - // This test sets up a non-bundled call and negotiates bundling at the same // time as starting an ICE restart. When bundling is in effect in the restart, // the DTLS-SRTP context should be successfully reset. @@ -775,7 +730,7 @@ TEST_P(PeerConnectionIntegrationTest, AnswererRejectsAudioSection) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // Only add video track for callee, and set offer_to_receive_audio to 0, so // it will reject the audio m= section completely. PeerConnectionInterface::RTCOfferAnswerOptions options; @@ -819,7 +774,7 @@ TEST_P(PeerConnectionIntegrationTest, AnswererRejectsVideoSection) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // Only add audio track for callee, and set offer_to_receive_video to 0, so // it will reject the video m= section completely. PeerConnectionInterface::RTCOfferAnswerOptions options; @@ -866,7 +821,7 @@ TEST_P(PeerConnectionIntegrationTest, AnswererRejectsAudioAndVideoSections) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // Don't give the callee any tracks, and set offer_to_receive_X to 0, so it // will reject both audio and video m= sections. PeerConnectionInterface::RTCOfferAnswerOptions options; @@ -914,7 +869,7 @@ TEST_P(PeerConnectionIntegrationTest, VideoRejectedInSubsequentOffer) { ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Renegotiate, rejecting the video m= section. - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { caller()->SetGeneratedSdpMunger( [](cricket::SessionDescription* description) { for (cricket::ContentInfo& content : description->contents()) { @@ -1326,8 +1281,9 @@ TEST_P(PeerConnectionIntegrationTest, GetBytesReceivedStatsWithOldStatsApi) { for (const auto& receiver : callee()->pc()->GetReceivers()) { // We received frames, so we definitely should have nonzero "received bytes" // stats at this point. - EXPECT_GT(callee()->OldGetStatsForTrack(receiver->track())->BytesReceived(), - 0); + EXPECT_GT( + callee()->OldGetStatsForTrack(receiver->track().get())->BytesReceived(), + 0); } } @@ -1348,34 +1304,8 @@ TEST_P(PeerConnectionIntegrationTest, GetBytesSentStatsWithOldStatsApi) { // The callee received frames, so we definitely should have nonzero "sent // bytes" stats at this point. - EXPECT_GT(caller()->OldGetStatsForTrack(audio_track)->BytesSent(), 0); - EXPECT_GT(caller()->OldGetStatsForTrack(video_track)->BytesSent(), 0); -} - -// Test that we can get capture start ntp time. -TEST_P(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { - ASSERT_TRUE(CreatePeerConnectionWrappers()); - ConnectFakeSignaling(); - caller()->AddAudioTrack(); - - callee()->AddAudioTrack(); - - // Do offer/answer, wait for the callee to receive some frames. - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - - // Get the remote audio track created on the receiver, so they can be used as - // GetStats filters. - auto receivers = callee()->pc()->GetReceivers(); - ASSERT_EQ(1u, receivers.size()); - auto remote_audio_track = receivers[0]->track(); - - // Get the audio output level stats. Note that the level is not available - // until an RTCP packet has been received. - EXPECT_TRUE_WAIT( - callee()->OldGetStatsForTrack(remote_audio_track)->CaptureStartNtpTime() > - 0, - 2 * kMaxWaitForFramesMs); + EXPECT_GT(caller()->OldGetStatsForTrack(audio_track.get())->BytesSent(), 0); + EXPECT_GT(caller()->OldGetStatsForTrack(video_track.get())->BytesSent(), 0); } // Test that the track ID is associated with all local and remote SSRC stats @@ -2001,106 +1931,6 @@ class PeerConnectionIntegrationIceStatesTestWithFakeClock #if !defined(THREAD_SANITIZER) // This test provokes TSAN errors. bugs.webrtc.org/11282 -// Tests that the PeerConnection goes through all the ICE gathering/connection -// states over the duration of the call. This includes Disconnected and Failed -// states, induced by putting a firewall between the peers and waiting for them -// to time out. -TEST_P(PeerConnectionIntegrationIceStatesTestWithFakeClock, VerifyIceStates) { - const SocketAddress kStunServerAddress = - SocketAddress("99.99.99.1", cricket::STUN_SERVER_PORT); - StartStunServer(kStunServerAddress); - - PeerConnectionInterface::RTCConfiguration config; - PeerConnectionInterface::IceServer ice_stun_server; - ice_stun_server.urls.push_back( - "stun:" + kStunServerAddress.HostAsURIString() + ":" + - kStunServerAddress.PortAsString()); - config.servers.push_back(ice_stun_server); - - ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config)); - ConnectFakeSignaling(); - SetPortAllocatorFlags(); - SetUpNetworkInterfaces(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); - - // Initial state before anything happens. - ASSERT_EQ(PeerConnectionInterface::kIceGatheringNew, - caller()->ice_gathering_state()); - ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, - caller()->ice_connection_state()); - ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, - caller()->standardized_ice_connection_state()); - - // Start the call by creating the offer, setting it as the local description, - // then sending it to the peer who will respond with an answer. This happens - // asynchronously so that we can watch the states as it runs in the - // background. - caller()->CreateAndSetAndSignalOffer(); - - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, - caller()->ice_connection_state(), kDefaultTimeout, - FakeClock()); - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, - caller()->standardized_ice_connection_state(), - kDefaultTimeout, FakeClock()); - - // Verify that the observer was notified of the intermediate transitions. - EXPECT_THAT(caller()->ice_connection_state_history(), - ElementsAre(PeerConnectionInterface::kIceConnectionChecking, - PeerConnectionInterface::kIceConnectionConnected, - PeerConnectionInterface::kIceConnectionCompleted)); - EXPECT_THAT(caller()->standardized_ice_connection_state_history(), - ElementsAre(PeerConnectionInterface::kIceConnectionChecking, - PeerConnectionInterface::kIceConnectionConnected, - PeerConnectionInterface::kIceConnectionCompleted)); - EXPECT_THAT( - caller()->peer_connection_state_history(), - ElementsAre(PeerConnectionInterface::PeerConnectionState::kConnecting, - PeerConnectionInterface::PeerConnectionState::kConnected)); - EXPECT_THAT(caller()->ice_gathering_state_history(), - ElementsAre(PeerConnectionInterface::kIceGatheringGathering, - PeerConnectionInterface::kIceGatheringComplete)); - - // Block connections to/from the caller and wait for ICE to become - // disconnected. - for (const auto& caller_address : CallerAddresses()) { - firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); - } - RTC_LOG(LS_INFO) << "Firewall rules applied"; - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, - caller()->ice_connection_state(), kDefaultTimeout, - FakeClock()); - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, - caller()->standardized_ice_connection_state(), - kDefaultTimeout, FakeClock()); - - // Let ICE re-establish by removing the firewall rules. - firewall()->ClearRules(); - RTC_LOG(LS_INFO) << "Firewall rules cleared"; - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, - caller()->ice_connection_state(), kDefaultTimeout, - FakeClock()); - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, - caller()->standardized_ice_connection_state(), - kDefaultTimeout, FakeClock()); - - // According to RFC7675, if there is no response within 30 seconds then the - // peer should consider the other side to have rejected the connection. This - // is signaled by the state transitioning to "failed". - constexpr int kConsentTimeout = 30000; - for (const auto& caller_address : CallerAddresses()) { - firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); - } - RTC_LOG(LS_INFO) << "Firewall rules applied again"; - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, - caller()->ice_connection_state(), kConsentTimeout, - FakeClock()); - ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, - caller()->standardized_ice_connection_state(), - kConsentTimeout, FakeClock()); -} - // Tests that if the connection doesn't get set up properly we eventually reach // the "failed" iceConnectionState. TEST_P(PeerConnectionIntegrationIceStatesTestWithFakeClock, @@ -2187,7 +2017,7 @@ constexpr uint32_t kFlagsIPv4Stun = INSTANTIATE_TEST_SUITE_P( PeerConnectionIntegrationTest, PeerConnectionIntegrationIceStatesTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), std::make_pair("IPv4 with STUN", kFlagsIPv4Stun)))); @@ -2195,7 +2025,7 @@ INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P( PeerConnectionIntegrationTest, PeerConnectionIntegrationIceStatesTestWithFakeClock, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), std::make_pair("IPv4 with STUN", kFlagsIPv4Stun)))); @@ -2359,7 +2189,7 @@ TEST_P(PeerConnectionIntegrationTest, // Negotiate again, disabling the video "m=" section (the callee will set the // port to 0 due to offer_to_receive_video = 0). - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_video = 0; callee()->SetOfferAnswerOptions(options); @@ -2380,7 +2210,7 @@ TEST_P(PeerConnectionIntegrationTest, // Enable video and do negotiation again, making sure video is received // end-to-end, also adding media stream to callee. - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { PeerConnectionInterface::RTCOfferAnswerOptions options; options.offer_to_receive_video = 1; callee()->SetOfferAnswerOptions(options); @@ -2427,10 +2257,14 @@ TEST_F(PeerConnectionIntegrationTestPlanB, EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected, callee()->ice_connection_state(), kMaxWaitForFramesMs); // Now set the tracks, and expect frames to immediately start flowing. - EXPECT_TRUE(caller_audio_sender->SetTrack(caller()->CreateLocalAudioTrack())); - EXPECT_TRUE(caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack())); - EXPECT_TRUE(callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack())); - EXPECT_TRUE(callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack())); + EXPECT_TRUE( + caller_audio_sender->SetTrack(caller()->CreateLocalAudioTrack().get())); + EXPECT_TRUE( + caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack().get())); + EXPECT_TRUE( + callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack().get())); + EXPECT_TRUE( + callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack().get())); MediaExpectations media_expectations; media_expectations.ExpectBidirectionalAudioAndVideo(); ASSERT_TRUE(ExpectNewFrames(media_expectations)); @@ -2466,10 +2300,14 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, // Now set the tracks, and expect frames to immediately start flowing. auto callee_audio_sender = callee()->pc()->GetSenders()[0]; auto callee_video_sender = callee()->pc()->GetSenders()[1]; - ASSERT_TRUE(caller_audio_sender->SetTrack(caller()->CreateLocalAudioTrack())); - ASSERT_TRUE(caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack())); - ASSERT_TRUE(callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack())); - ASSERT_TRUE(callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack())); + ASSERT_TRUE( + caller_audio_sender->SetTrack(caller()->CreateLocalAudioTrack().get())); + ASSERT_TRUE( + caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack().get())); + ASSERT_TRUE( + callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack().get())); + ASSERT_TRUE( + callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack().get())); MediaExpectations media_expectations; media_expectations.ExpectBidirectionalAudioAndVideo(); ASSERT_TRUE(ExpectNewFrames(media_expectations)); @@ -2754,71 +2592,6 @@ TEST_P(PeerConnectionIntegrationTest, EXPECT_GT(client_2_cert_verifier->call_count_, 0u); } -TEST_P(PeerConnectionIntegrationTest, - SSLCertificateVerifierFailureUsedForTurnConnectionsFailsConnection) { - static const rtc::SocketAddress turn_server_internal_address{"88.88.88.0", - 3478}; - static const rtc::SocketAddress turn_server_external_address{"88.88.88.1", 0}; - - // Enable TCP-TLS for the fake turn server. We need to pass in 88.88.88.0 so - // that host name verification passes on the fake certificate. - CreateTurnServer(turn_server_internal_address, turn_server_external_address, - cricket::PROTO_TLS, "88.88.88.0"); - - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.urls.push_back("turns:88.88.88.0:3478?transport=tcp"); - ice_server.username = "test"; - ice_server.password = "test"; - - PeerConnectionInterface::RTCConfiguration client_1_config; - client_1_config.servers.push_back(ice_server); - client_1_config.type = webrtc::PeerConnectionInterface::kRelay; - - PeerConnectionInterface::RTCConfiguration client_2_config; - client_2_config.servers.push_back(ice_server); - // Setting the type to kRelay forces the connection to go through a TURN - // server. - client_2_config.type = webrtc::PeerConnectionInterface::kRelay; - - // Get a copy to the pointer so we can verify calls later. - rtc::TestCertificateVerifier* client_1_cert_verifier = - new rtc::TestCertificateVerifier(); - client_1_cert_verifier->verify_certificate_ = false; - rtc::TestCertificateVerifier* client_2_cert_verifier = - new rtc::TestCertificateVerifier(); - client_2_cert_verifier->verify_certificate_ = false; - - // Create the dependencies with the test certificate verifier. - webrtc::PeerConnectionDependencies client_1_deps(nullptr); - client_1_deps.tls_cert_verifier = - std::unique_ptr(client_1_cert_verifier); - webrtc::PeerConnectionDependencies client_2_deps(nullptr); - client_2_deps.tls_cert_verifier = - std::unique_ptr(client_2_cert_verifier); - - ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndDeps( - client_1_config, std::move(client_1_deps), client_2_config, - std::move(client_2_deps))); - ConnectFakeSignaling(); - - // Set "offer to receive audio/video" without adding any tracks, so we just - // set up ICE/DTLS with no media. - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_audio = 1; - options.offer_to_receive_video = 1; - caller()->SetOfferAnswerOptions(options); - caller()->CreateAndSetAndSignalOffer(); - bool wait_res = true; - // TODO(bugs.webrtc.org/9219): When IceConnectionState is implemented - // properly, should be able to just wait for a state of "failed" instead of - // waiting a fixed 10 seconds. - WAIT_(DtlsConnected(), kDefaultTimeout, wait_res); - ASSERT_FALSE(wait_res); - - EXPECT_GT(client_1_cert_verifier->call_count_, 0u); - EXPECT_GT(client_2_cert_verifier->call_count_, 0u); -} - // Test that the injected ICE transport factory is used to create ICE transports // for WebRTC connections. TEST_P(PeerConnectionIntegrationTest, IceTransportFactoryUsedForConnections) { @@ -2834,7 +2607,7 @@ TEST_P(PeerConnectionIntegrationTest, IceTransportFactoryUsedForConnections) { ASSERT_TRUE(wrapper); wrapper->CreateDataChannel(); auto observer = rtc::make_ref_counted(); - wrapper->pc()->SetLocalDescription(observer, + wrapper->pc()->SetLocalDescription(observer.get(), wrapper->CreateOfferAndWait().release()); } @@ -3319,7 +3092,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, caller()->AddVideoTrack(); callee()->AddVideoTrack(); auto observer = rtc::make_ref_counted(); - callee()->pc()->SetLocalDescription(observer, + callee()->pc()->SetLocalDescription(observer.get(), callee()->CreateOfferAndWait().release()); EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout); caller()->CreateAndSetAndSignalOffer(); // Implicit rollback. @@ -3337,7 +3110,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, auto sld_observer = rtc::make_ref_counted(); - callee()->pc()->SetLocalDescription(sld_observer, + callee()->pc()->SetLocalDescription(sld_observer.get(), callee()->CreateOfferAndWait().release()); EXPECT_TRUE_WAIT(sld_observer->called(), kDefaultTimeout); EXPECT_EQ(sld_observer->error(), ""); @@ -3345,7 +3118,7 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, auto srd_observer = rtc::make_ref_counted(); callee()->pc()->SetRemoteDescription( - srd_observer, caller()->CreateOfferAndWait().release()); + srd_observer.get(), caller()->CreateOfferAndWait().release()); EXPECT_TRUE_WAIT(srd_observer->called(), kDefaultTimeout); EXPECT_EQ(srd_observer->error(), ""); @@ -3513,15 +3286,21 @@ TEST_F(PeerConnectionIntegrationTestUnifiedPlan, } } -INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest, - PeerConnectionIntegrationTest, - Values(SdpSemantics::kPlanB, - SdpSemantics::kUnifiedPlan)); +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationTest, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), + Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", + "WebRTC-FrameBuffer3/arm:SyncDecoding/"))); -INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest, - PeerConnectionIntegrationTestWithFakeClock, - Values(SdpSemantics::kPlanB, - SdpSemantics::kUnifiedPlan)); +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationTestWithFakeClock, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), + Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", + "WebRTC-FrameBuffer3/arm:SyncDecoding/"))); // Tests that verify interoperability between Plan B and Unified Plan // PeerConnections. @@ -3534,7 +3313,7 @@ class PeerConnectionIntegrationInteropTest // because we specify not to use the test semantics when creating // PeerConnectionIntegrationWrappers. PeerConnectionIntegrationInteropTest() - : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB), + : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED), caller_semantics_(std::get<0>(GetParam())), callee_semantics_(std::get<1>(GetParam())) {} @@ -3639,17 +3418,40 @@ TEST_P(PeerConnectionIntegrationInteropTest, ASSERT_TRUE(ExpectNewFrames(media_expectations)); } +TEST_P(PeerConnectionIntegrationTest, NewTracksDoNotCauseNewCandidates) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->AddAudioVideoTracks(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + ASSERT_TRUE_WAIT(DtlsConnected(), kDefaultTimeout); + caller()->ExpectCandidates(0); + callee()->ExpectCandidates(0); + caller()->AddAudioTrack(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); +} + +TEST_P(PeerConnectionIntegrationTest, MediaCallWithoutMediaEngineFails) { + ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + // AddTrack should fail. + EXPECT_FALSE( + caller()->pc()->AddTrack(caller()->CreateLocalAudioTrack(), {}).ok()); +} + INSTANTIATE_TEST_SUITE_P( PeerConnectionIntegrationTest, PeerConnectionIntegrationInteropTest, - Values(std::make_tuple(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), - std::make_tuple(SdpSemantics::kUnifiedPlan, SdpSemantics::kPlanB))); + Values(std::make_tuple(SdpSemantics::kPlanB_DEPRECATED, + SdpSemantics::kUnifiedPlan), + std::make_tuple(SdpSemantics::kUnifiedPlan, + SdpSemantics::kPlanB_DEPRECATED))); // Test that if the Unified Plan side offers two video tracks then the Plan B // side will only see the first one and ignore the second. TEST_F(PeerConnectionIntegrationTestPlanB, TwoVideoUnifiedPlanToNoMediaPlanB) { ASSERT_TRUE(CreatePeerConnectionWrappersWithSdpSemantics( - SdpSemantics::kUnifiedPlan, SdpSemantics::kPlanB)); + SdpSemantics::kUnifiedPlan, SdpSemantics::kPlanB_DEPRECATED)); ConnectFakeSignaling(); auto first_sender = caller()->AddVideoTrack(); caller()->AddVideoTrack(); diff --git a/pc/peer_connection_interface_unittest.cc b/pc/peer_connection_interface_unittest.cc index 0d9f419149..20badb3d50 100644 --- a/pc/peer_connection_interface_unittest.cc +++ b/pc/peer_connection_interface_unittest.cc @@ -12,9 +12,7 @@ #include #include -#include -#include #include #include #include @@ -22,32 +20,26 @@ #include "absl/strings/str_replace.h" #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/call/call_factory_interface.h" #include "api/create_peerconnection_factory.h" #include "api/data_channel_interface.h" #include "api/jsep.h" -#include "api/jsep_session_description.h" #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtc_event_log/rtc_event_log_factory.h" #include "api/rtc_event_log_output.h" -#include "api/rtc_event_log_output_file.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" -#include "api/rtp_transceiver_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" #include "api/transport/field_trial_based_config.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/codec.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" @@ -68,8 +60,8 @@ #include "pc/media_stream.h" #include "pc/peer_connection.h" #include "pc/peer_connection_factory.h" -#include "pc/rtc_stats_collector.h" #include "pc/rtp_sender.h" +#include "pc/rtp_sender_proxy.h" #include "pc/session_description.h" #include "pc/stream_collection.h" #include "pc/test/fake_audio_capture_module.h" @@ -79,17 +71,14 @@ #include "pc/test/test_sdp_strings.h" #include "pc/video_track.h" #include "rtc_base/checks.h" -#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/socket_address.h" #include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/gtest.h" -#include "test/testsupport/file_utils.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" @@ -136,8 +125,7 @@ static const char kSdpStringWithStream1PlanB[] = "a=rtcp-mux\r\n" "a=rtpmap:103 ISAC/16000\r\n" "a=ssrc:1 cname:stream1\r\n" - "a=ssrc:1 mslabel:stream1\r\n" - "a=ssrc:1 label:audiotrack0\r\n" + "a=ssrc:1 msid:stream1 audiotrack0\r\n" "m=video 1 RTP/AVPF 120\r\n" "a=ice-ufrag:e5785931\r\n" "a=ice-pwd:36fb7878390db89481c1d46daa4278d8\r\n" @@ -148,11 +136,10 @@ static const char kSdpStringWithStream1PlanB[] = "a=rtcp-mux\r\n" "a=rtpmap:120 VP8/90000\r\n" "a=ssrc:2 cname:stream1\r\n" - "a=ssrc:2 mslabel:stream1\r\n" - "a=ssrc:2 label:videotrack0\r\n"; -// Same string as above but with the MID changed to the Unified Plan default. -// This is needed so that this SDP can be used as an answer for a Unified Plan -// offer. + "a=ssrc:2 msid:stream1 videotrack0\r\n"; +// Same string as above but with the MID changed to the Unified Plan default and +// a=msid added. This is needed so that this SDP can be used as an answer for a +// Unified Plan offer. static const char kSdpStringWithStream1UnifiedPlan[] = "v=0\r\n" "o=- 0 0 IN IP4 127.0.0.1\r\n" @@ -167,9 +154,8 @@ static const char kSdpStringWithStream1UnifiedPlan[] = "a=sendrecv\r\n" "a=rtcp-mux\r\n" "a=rtpmap:103 ISAC/16000\r\n" + "a=msid:stream1 audiotrack0\r\n" "a=ssrc:1 cname:stream1\r\n" - "a=ssrc:1 mslabel:stream1\r\n" - "a=ssrc:1 label:audiotrack0\r\n" "m=video 1 RTP/AVPF 120\r\n" "a=ice-ufrag:e5785931\r\n" "a=ice-pwd:36fb7878390db89481c1d46daa4278d8\r\n" @@ -179,9 +165,8 @@ static const char kSdpStringWithStream1UnifiedPlan[] = "a=sendrecv\r\n" "a=rtcp-mux\r\n" "a=rtpmap:120 VP8/90000\r\n" - "a=ssrc:2 cname:stream1\r\n" - "a=ssrc:2 mslabel:stream1\r\n" - "a=ssrc:2 label:videotrack0\r\n"; + "a=msid:stream1 videotrack0\r\n" + "a=ssrc:2 cname:stream1\r\n"; // Reference SDP with a MediaStream with label "stream1" and audio track with // id "audio_1"; @@ -199,8 +184,7 @@ static const char kSdpStringWithStream1AudioTrackOnly[] = "a=sendrecv\r\n" "a=rtpmap:103 ISAC/16000\r\n" "a=ssrc:1 cname:stream1\r\n" - "a=ssrc:1 mslabel:stream1\r\n" - "a=ssrc:1 label:audiotrack0\r\n" + "a=ssrc:1 msid:stream1 audiotrack0\r\n" "a=rtcp-mux\r\n"; // Reference SDP with two MediaStreams with label "stream1" and "stream2. Each @@ -441,8 +425,6 @@ static const char kDtlsSdesFallbackSdp[] = "a=rtcp-mux\r\n" "a=mid:audio\r\n" "a=ssrc:1 cname:stream1\r\n" - "a=ssrc:1 mslabel:stream1\r\n" - "a=ssrc:1 label:audiotrack0\r\n" "a=ice-ufrag:e5785931\r\n" "a=ice-pwd:36fb7878390db89481c1d46daa4278d8\r\n" "a=rtpmap:0 pcmu/8000\r\n" @@ -760,10 +742,13 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - pc_ = pc_factory_->CreatePeerConnection( - modified_config, std::move(port_allocator), std::move(cert_generator), - &observer_); - ASSERT_TRUE(pc_.get() != NULL); + PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.cert_generator = std::move(cert_generator); + pc_dependencies.allocator = std::move(port_allocator); + auto result = pc_factory_->CreatePeerConnectionOrError( + modified_config, std::move(pc_dependencies)); + ASSERT_TRUE(result.ok()); + pc_ = result.MoveValue(); observer_.SetPeerConnectionInterface(pc_.get()); EXPECT_EQ(PeerConnectionInterface::kStable, observer_.state_); } @@ -774,9 +759,10 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { server.uri = uri; config.servers.push_back(server); config.sdp_semantics = sdp_semantics_; - rtc::scoped_refptr pc = - pc_factory_->CreatePeerConnection(config, nullptr, nullptr, &observer_); - EXPECT_EQ(nullptr, pc); + PeerConnectionDependencies pc_dependencies(&observer_); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + EXPECT_FALSE(result.ok()); } void CreatePeerConnectionExpectFail( @@ -786,9 +772,10 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { server.password = kTurnPassword; config.servers.push_back(server); config.sdp_semantics = sdp_semantics_; - rtc::scoped_refptr pc = - pc_factory_->CreatePeerConnection(config, nullptr, nullptr, &observer_); - EXPECT_EQ(nullptr, pc); + PeerConnectionDependencies pc_dependencies(&observer_); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + EXPECT_FALSE(result.ok()); } void CreatePeerConnectionWithDifferentConfigurations() { @@ -816,13 +803,14 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { } void ReleasePeerConnection() { - pc_ = NULL; - observer_.SetPeerConnectionInterface(NULL); + pc_ = nullptr; + observer_.SetPeerConnectionInterface(nullptr); } rtc::scoped_refptr CreateVideoTrack( const std::string& label) { - return pc_factory_->CreateVideoTrack(label, FakeVideoTrackSource::Create()); + return pc_factory_->CreateVideoTrack(label, + FakeVideoTrackSource::Create().get()); } void AddVideoTrack(const std::string& track_label, @@ -836,7 +824,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); stream->AddTrack(CreateVideoTrack(label + "v0")); - ASSERT_TRUE(pc_->AddStream(stream)); + ASSERT_TRUE(pc_->AddStream(stream.get())); } rtc::scoped_refptr CreateAudioTrack( @@ -855,7 +843,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); stream->AddTrack(CreateAudioTrack(label + "a0")); - ASSERT_TRUE(pc_->AddStream(stream)); + ASSERT_TRUE(pc_->AddStream(stream.get())); } void AddAudioVideoStream(const std::string& stream_id, @@ -866,7 +854,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { pc_factory_->CreateLocalMediaStream(stream_id)); stream->AddTrack(CreateAudioTrack(audio_track_label)); stream->AddTrack(CreateVideoTrack(video_track_label)); - ASSERT_TRUE(pc_->AddStream(stream)); + ASSERT_TRUE(pc_->AddStream(stream.get())); } rtc::scoped_refptr GetFirstReceiverOfType( @@ -885,9 +873,11 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { auto observer = rtc::make_ref_counted(); if (offer) { - pc_->CreateOffer(observer, options ? *options : RTCOfferAnswerOptions()); + pc_->CreateOffer(observer.get(), + options ? *options : RTCOfferAnswerOptions()); } else { - pc_->CreateAnswer(observer, options ? *options : RTCOfferAnswerOptions()); + pc_->CreateAnswer(observer.get(), + options ? *options : RTCOfferAnswerOptions()); } EXPECT_EQ_WAIT(true, observer->called(), kTimeout); *desc = observer->MoveDescription(); @@ -909,9 +899,9 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { bool local) { auto observer = rtc::make_ref_counted(); if (local) { - pc_->SetLocalDescription(observer, desc.release()); + pc_->SetLocalDescription(observer.get(), desc.release()); } else { - pc_->SetRemoteDescription(observer, desc.release()); + pc_->SetRemoteDescription(observer.get(), desc.release()); } if (pc_->signaling_state() != PeerConnectionInterface::kClosed) { EXPECT_EQ_WAIT(true, observer->called(), kTimeout); @@ -934,7 +924,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { // be required. bool DoGetStats(MediaStreamTrackInterface* track) { auto observer = rtc::make_ref_counted(); - if (!pc_->GetStats(observer, track, + if (!pc_->GetStats(observer.get(), track, PeerConnectionInterface::kStatsOutputLevelStandard)) return false; EXPECT_TRUE_WAIT(observer->called(), kTimeout); @@ -945,7 +935,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { bool DoGetRTCStats() { auto callback = rtc::make_ref_counted(); - pc_->GetStats(callback); + pc_->GetStats(callback.get()); EXPECT_TRUE_WAIT(callback->called(), kTimeout); return callback->called(); } @@ -953,7 +943,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { void InitiateCall() { CreatePeerConnectionWithoutDtls(); // Create a local stream with audio&video tracks. - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { AddAudioVideoStream(kStreamId1, "audio_track", "video_track"); } else { // Unified Plan does not support AddStream, so just add an audio and video @@ -969,12 +959,12 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { const cricket::MediaContentDescription* desc = cricket::GetFirstAudioContentDescription( pc_->remote_description()->description()); - ASSERT_TRUE(desc != NULL); + ASSERT_TRUE(desc != nullptr); EXPECT_GT(desc->rtp_header_extensions().size(), 0u); desc = cricket::GetFirstVideoContentDescription( pc_->remote_description()->description()); - ASSERT_TRUE(desc != NULL); + ASSERT_TRUE(desc != nullptr); EXPECT_GT(desc->rtp_header_extensions().size(), 0u); } @@ -1121,21 +1111,21 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { if (number_of_audio_tracks > 0) { sdp_ms1 += std::string(kSdpStringAudio); sdp_ms1 += std::string(kSdpStringMs1Audio0); - AddAudioTrack(kAudioTracks[0], stream); + AddAudioTrack(kAudioTracks[0], stream.get()); } if (number_of_audio_tracks > 1) { sdp_ms1 += kSdpStringMs1Audio1; - AddAudioTrack(kAudioTracks[1], stream); + AddAudioTrack(kAudioTracks[1], stream.get()); } if (number_of_video_tracks > 0) { sdp_ms1 += std::string(kSdpStringVideo); sdp_ms1 += std::string(kSdpStringMs1Video0); - AddVideoTrack(kVideoTracks[0], stream); + AddVideoTrack(kVideoTracks[0], stream.get()); } if (number_of_video_tracks > 1) { sdp_ms1 += kSdpStringMs1Video1; - AddVideoTrack(kVideoTracks[1], stream); + AddVideoTrack(kVideoTracks[1], stream.get()); } return std::unique_ptr( @@ -1201,7 +1191,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { RTC_DCHECK(pc_); auto observer = rtc::make_ref_counted(); - pc_->CreateOffer(observer, offer_answer_options); + pc_->CreateOffer(observer.get(), offer_answer_options); EXPECT_EQ_WAIT(true, observer->called(), kTimeout); return observer->MoveDescription(); } @@ -1246,7 +1236,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { } const char* GetSdpStringWithStream1() const { - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { return kSdpStringWithStream1PlanB; } else { return kSdpStringWithStream1UnifiedPlan; @@ -1254,7 +1244,7 @@ class PeerConnectionInterfaceBaseTest : public ::testing::Test { } const char* GetSdpStringWithStream1And2() const { - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { return kSdpStringWithStream1And2PlanB; } else { return kSdpStringWithStream1And2UnifiedPlan; @@ -1285,7 +1275,7 @@ class PeerConnectionInterfaceTestPlanB : public PeerConnectionInterfaceBaseTest { protected: PeerConnectionInterfaceTestPlanB() - : PeerConnectionInterfaceBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionInterfaceBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; // Generate different CNAMEs when PeerConnections are created. @@ -1395,11 +1385,12 @@ TEST_P(PeerConnectionInterfaceTest, webrtc::CreateBuiltinVideoEncoderFactory(), webrtc::CreateBuiltinVideoDecoderFactory(), nullptr /* audio_mixer */, nullptr /* audio_processing */)); - rtc::scoped_refptr pc( - pc_factory->CreatePeerConnection(config, std::move(port_allocator), - nullptr, &observer_)); - EXPECT_TRUE(pc.get()); - observer_.SetPeerConnectionInterface(pc.get()); + PeerConnectionDependencies pc_dependencies(&observer_); + pc_dependencies.allocator = std::move(port_allocator); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, std::move(pc_dependencies)); + EXPECT_TRUE(result.ok()); + observer_.SetPeerConnectionInterface(result.value().get()); // Now validate that the config fields set above were applied to the // PortAllocator, as flags or otherwise. @@ -1463,10 +1454,10 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AddStreams) { rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(kStreamId3)); rtc::scoped_refptr audio_track( - pc_factory_->CreateAudioTrack(kStreamId3, - static_cast(NULL))); - stream->AddTrack(audio_track.get()); - EXPECT_TRUE(pc_->AddStream(stream)); + pc_factory_->CreateAudioTrack( + kStreamId3, static_cast(nullptr))); + stream->AddTrack(audio_track); + EXPECT_TRUE(pc_->AddStream(stream.get())); EXPECT_EQ(3u, pc_->local_streams()->count()); // Remove the third stream. @@ -1599,7 +1590,7 @@ TEST_P(PeerConnectionInterfaceTest, AddTrackWithoutStream) { EXPECT_EQ(audio_track, audio_sender->track()); EXPECT_EQ("video_track", video_sender->id()); EXPECT_EQ(video_track, video_sender->track()); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // If the ID is truly a random GUID, it should be infinitely unlikely they // will be the same. EXPECT_NE(video_sender->stream_ids(), audio_sender->stream_ids()); @@ -1808,7 +1799,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, AddTrackAfterAddStream) { // Add video track to the audio-only stream. rtc::scoped_refptr video_track( CreateVideoTrack("video_label")); - stream->AddTrack(video_track.get()); + stream->AddTrack(video_track); std::unique_ptr offer; ASSERT_TRUE(DoCreateOffer(&offer, nullptr)); @@ -1863,7 +1854,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { ASSERT_LT(0u, pc_->GetReceivers().size()); rtc::scoped_refptr remote_audio = pc_->GetReceivers()[0]->track(); - EXPECT_TRUE(DoGetStats(remote_audio)); + EXPECT_TRUE(DoGetStats(remote_audio.get())); // Remove the stream. Since we are sending to our selves the local // and the remote stream is the same. @@ -1873,7 +1864,7 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { // Test that we still can get statistics for the old track. Even if it is not // sent any longer. - EXPECT_TRUE(DoGetStats(remote_audio)); + EXPECT_TRUE(DoGetStats(remote_audio.get())); } // Test that we can get stats on a video track. @@ -1881,15 +1872,15 @@ TEST_P(PeerConnectionInterfaceTest, GetStatsForVideoTrack) { InitiateCall(); auto video_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_VIDEO); ASSERT_TRUE(video_receiver); - EXPECT_TRUE(DoGetStats(video_receiver->track())); + EXPECT_TRUE(DoGetStats(video_receiver->track().get())); } // Test that we don't get statistics for an invalid track. TEST_P(PeerConnectionInterfaceTest, GetStatsForInvalidTrack) { InitiateCall(); rtc::scoped_refptr unknown_audio_track( - pc_factory_->CreateAudioTrack("unknown track", NULL)); - EXPECT_FALSE(DoGetStats(unknown_audio_track)); + pc_factory_->CreateAudioTrack("unknown track", nullptr)); + EXPECT_FALSE(DoGetStats(unknown_audio_track.get())); } TEST_P(PeerConnectionInterfaceTest, GetRTCStatsBeforeAndAfterCalling) { @@ -1913,31 +1904,30 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannel) { CreatePeerConnection(rtc_config); webrtc::DataChannelInit config; - rtc::scoped_refptr channel = - pc_->CreateDataChannel("1", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_TRUE(channel->reliable()); + auto channel = pc_->CreateDataChannelOrError("1", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_TRUE(channel.value()->reliable()); EXPECT_TRUE(observer_.renegotiation_needed_); observer_.renegotiation_needed_ = false; config.ordered = false; - channel = pc_->CreateDataChannel("2", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_TRUE(channel->reliable()); + channel = pc_->CreateDataChannelOrError("2", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_TRUE(channel.value()->reliable()); EXPECT_FALSE(observer_.renegotiation_needed_); config.ordered = true; config.maxRetransmits = 0; - channel = pc_->CreateDataChannel("3", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_FALSE(channel->reliable()); + channel = pc_->CreateDataChannelOrError("3", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_FALSE(channel.value()->reliable()); EXPECT_FALSE(observer_.renegotiation_needed_); config.maxRetransmits = absl::nullopt; config.maxRetransmitTime = 0; - channel = pc_->CreateDataChannel("4", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_FALSE(channel->reliable()); + channel = pc_->CreateDataChannelOrError("4", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_FALSE(channel.value()->reliable()); EXPECT_FALSE(observer_.renegotiation_needed_); } @@ -1950,9 +1940,8 @@ TEST_P(PeerConnectionInterfaceTest, CreateSctpDataChannelWithMinusOne) { webrtc::DataChannelInit config; config.maxRetransmitTime = -1; config.maxRetransmits = -1; - rtc::scoped_refptr channel = - pc_->CreateDataChannel("1", &config); - EXPECT_TRUE(channel != NULL); + auto channel = pc_->CreateDataChannelOrError("1", &config); + EXPECT_TRUE(channel.ok()); } // This tests that no data channel is returned if both maxRetransmits and @@ -1967,9 +1956,8 @@ TEST_P(PeerConnectionInterfaceTest, config.maxRetransmits = 0; config.maxRetransmitTime = 0; - rtc::scoped_refptr channel = - pc_->CreateDataChannel(label, &config); - EXPECT_TRUE(channel == NULL); + auto channel = pc_->CreateDataChannelOrError(label, &config); + EXPECT_FALSE(channel.ok()); } // The test verifies that creating a SCTP data channel with an id already in use @@ -1980,27 +1968,26 @@ TEST_P(PeerConnectionInterfaceTest, CreatePeerConnection(rtc_config); webrtc::DataChannelInit config; - rtc::scoped_refptr channel; config.id = 1; config.negotiated = true; - channel = pc_->CreateDataChannel("1", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_EQ(1, channel->id()); + auto channel = pc_->CreateDataChannelOrError("1", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_EQ(1, channel.value()->id()); - channel = pc_->CreateDataChannel("x", &config); - EXPECT_TRUE(channel == NULL); + channel = pc_->CreateDataChannelOrError("x", &config); + EXPECT_FALSE(channel.ok()); config.id = cricket::kMaxSctpSid; config.negotiated = true; - channel = pc_->CreateDataChannel("max", &config); - EXPECT_TRUE(channel != NULL); - EXPECT_EQ(config.id, channel->id()); + channel = pc_->CreateDataChannelOrError("max", &config); + EXPECT_TRUE(channel.ok()); + EXPECT_EQ(config.id, channel.value()->id()); config.id = cricket::kMaxSctpSid + 1; config.negotiated = true; - channel = pc_->CreateDataChannel("x", &config); - EXPECT_TRUE(channel == NULL); + channel = pc_->CreateDataChannelOrError("x", &config); + EXPECT_FALSE(channel.ok()); } // Verifies that duplicated label is allowed for SCTP data channel. @@ -2009,16 +1996,13 @@ TEST_P(PeerConnectionInterfaceTest, SctpDuplicatedLabelAllowed) { CreatePeerConnection(rtc_config); std::string label = "test"; - rtc::scoped_refptr channel = - pc_->CreateDataChannel(label, nullptr); - EXPECT_NE(channel, nullptr); + auto channel = pc_->CreateDataChannelOrError(label, nullptr); + EXPECT_TRUE(channel.ok()); - rtc::scoped_refptr dup_channel = - pc_->CreateDataChannel(label, nullptr); - EXPECT_NE(dup_channel, nullptr); + auto dup_channel = pc_->CreateDataChannelOrError(label, nullptr); + EXPECT_TRUE(dup_channel.ok()); } - #ifdef WEBRTC_HAVE_SCTP // This tests that SCTP data channels can be rejected in an answer. TEST_P(PeerConnectionInterfaceTest, TestRejectSctpDataChannelInAnswer) @@ -2029,8 +2013,7 @@ TEST_P(PeerConnectionInterfaceTest, DISABLED_TestRejectSctpDataChannelInAnswer) RTCConfiguration rtc_config; CreatePeerConnection(rtc_config); - rtc::scoped_refptr offer_channel( - pc_->CreateDataChannel("offer_channel", NULL)); + auto offer_channel = pc_->CreateDataChannelOrError("offer_channel", NULL); CreateOfferAsLocalDescription(); @@ -2045,7 +2028,7 @@ TEST_P(PeerConnectionInterfaceTest, DISABLED_TestRejectSctpDataChannelInAnswer) data_info->rejected = true; DoSetRemoteDescription(std::move(answer)); - EXPECT_EQ(DataChannelInterface::kClosed, offer_channel->state()); + EXPECT_EQ(DataChannelInterface::kClosed, offer_channel.value()->state()); } // Test that we can create a session description from an SDP string from @@ -2061,22 +2044,22 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) { webrtc::kFireFoxSdpOffer, nullptr)); EXPECT_TRUE(DoSetSessionDescription(std::move(desc), false)); CreateAnswerAsLocalDescription(); - ASSERT_TRUE(pc_->local_description() != NULL); - ASSERT_TRUE(pc_->remote_description() != NULL); + ASSERT_TRUE(pc_->local_description() != nullptr); + ASSERT_TRUE(pc_->remote_description() != nullptr); const cricket::ContentInfo* content = cricket::GetFirstAudioContent(pc_->local_description()->description()); - ASSERT_TRUE(content != NULL); + ASSERT_TRUE(content != nullptr); EXPECT_FALSE(content->rejected); content = cricket::GetFirstVideoContent(pc_->local_description()->description()); - ASSERT_TRUE(content != NULL); + ASSERT_TRUE(content != nullptr); EXPECT_FALSE(content->rejected); #ifdef WEBRTC_HAVE_SCTP content = cricket::GetFirstDataContent(pc_->local_description()->description()); - ASSERT_TRUE(content != NULL); + ASSERT_TRUE(content != nullptr); EXPECT_FALSE(content->rejected); #endif } @@ -2108,15 +2091,15 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveUpdatedAudioOfferWithBadCodecs) { AddAudioTrack("audio_label"); CreateOfferAsLocalDescription(); - const char* answer_sdp = - (sdp_semantics_ == SdpSemantics::kPlanB ? webrtc::kAudioSdpPlanB - : webrtc::kAudioSdpUnifiedPlan); + const char* answer_sdp = (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED + ? webrtc::kAudioSdpPlanB + : webrtc::kAudioSdpUnifiedPlan); std::unique_ptr answer( webrtc::CreateSessionDescription(SdpType::kAnswer, answer_sdp, nullptr)); EXPECT_TRUE(DoSetSessionDescription(std::move(answer), false)); const char* reoffer_sdp = - (sdp_semantics_ == SdpSemantics::kPlanB + (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED ? webrtc::kAudioSdpWithUnsupportedCodecsPlanB : webrtc::kAudioSdpWithUnsupportedCodecsUnifiedPlan); std::unique_ptr updated_offer( @@ -2411,7 +2394,7 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { // With Plan B, verify the stream count. The analog with Unified Plan is the // RtpTransceiver count. - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { ASSERT_EQ(1u, pc_->local_streams()->count()); ASSERT_EQ(1u, pc_->remote_streams()->count()); } else { @@ -2426,7 +2409,7 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { EXPECT_EQ(PeerConnectionInterface::kIceGatheringComplete, pc_->ice_gathering_state()); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { EXPECT_EQ(1u, pc_->local_streams()->count()); EXPECT_EQ(1u, pc_->remote_streams()->count()); } else { @@ -2436,7 +2419,7 @@ TEST_P(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { auto audio_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_AUDIO); auto video_receiver = GetFirstReceiverOfType(cricket::MEDIA_TYPE_VIDEO); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { ASSERT_TRUE(audio_receiver); ASSERT_TRUE(video_receiver); // Track state may be updated asynchronously. @@ -2465,13 +2448,13 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CloseAndTestMethods) { pc_->Close(); - pc_->RemoveStream(local_stream); - EXPECT_FALSE(pc_->AddStream(local_stream)); + pc_->RemoveStream(local_stream.get()); + EXPECT_FALSE(pc_->AddStream(local_stream.get())); - EXPECT_TRUE(pc_->CreateDataChannel("test", NULL) == NULL); + EXPECT_FALSE(pc_->CreateDataChannelOrError("test", NULL).ok()); - EXPECT_TRUE(pc_->local_description() != NULL); - EXPECT_TRUE(pc_->remote_description() != NULL); + EXPECT_TRUE(pc_->local_description() != nullptr); + EXPECT_TRUE(pc_->remote_description() != nullptr); std::unique_ptr offer; EXPECT_FALSE(DoCreateOffer(&offer, nullptr)); @@ -2494,7 +2477,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CloseAndTestMethods) { TEST_P(PeerConnectionInterfaceTest, CloseAndGetStats) { InitiateCall(); pc_->Close(); - DoGetStats(NULL); + DoGetStats(nullptr); } // NOTE: The series of tests below come from what used to be @@ -2536,14 +2519,14 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CreateSessionDescriptionAndReference(1, 1); EXPECT_TRUE(DoSetRemoteDescription(std::move(desc_ms1))); EXPECT_TRUE(CompareStreamCollections(observer_.remote_streams(), - reference_collection_)); + reference_collection_.get())); // Add extra audio and video tracks to the same MediaStream. std::unique_ptr desc_ms1_two_tracks = CreateSessionDescriptionAndReference(2, 2); EXPECT_TRUE(DoSetRemoteDescription(std::move(desc_ms1_two_tracks))); EXPECT_TRUE(CompareStreamCollections(observer_.remote_streams(), - reference_collection_)); + reference_collection_.get())); rtc::scoped_refptr audio_track2 = observer_.remote_streams()->at(0)->GetAudioTracks()[1]; EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, audio_track2->state()); @@ -2554,14 +2537,14 @@ TEST_F(PeerConnectionInterfaceTestPlanB, // Remove the extra audio and video tracks. std::unique_ptr desc_ms2 = CreateSessionDescriptionAndReference(1, 1); - MockTrackObserver audio_track_observer(audio_track2); - MockTrackObserver video_track_observer(video_track2); + MockTrackObserver audio_track_observer(audio_track2.get()); + MockTrackObserver video_track_observer(video_track2.get()); EXPECT_CALL(audio_track_observer, OnChanged()).Times(Exactly(1)); EXPECT_CALL(video_track_observer, OnChanged()).Times(Exactly(1)); EXPECT_TRUE(DoSetRemoteDescription(std::move(desc_ms2))); EXPECT_TRUE(CompareStreamCollections(observer_.remote_streams(), - reference_collection_)); + reference_collection_.get())); // Track state may be updated asynchronously. EXPECT_EQ_WAIT(webrtc::MediaStreamTrackInterface::kEnded, audio_track2->state(), kTimeout); @@ -2931,7 +2914,7 @@ TEST_P(PeerConnectionInterfaceTest, // Change the ssrc of the audio and video track. cricket::MediaContentDescription* desc = cricket::GetFirstAudioContentDescription(modified_offer->description()); - ASSERT_TRUE(desc != NULL); + ASSERT_TRUE(desc != nullptr); for (StreamParams& stream : desc->mutable_streams()) { for (unsigned int& ssrc : stream.ssrcs) { ++ssrc; @@ -2940,7 +2923,7 @@ TEST_P(PeerConnectionInterfaceTest, desc = cricket::GetFirstVideoContentDescription(modified_offer->description()); - ASSERT_TRUE(desc != NULL); + ASSERT_TRUE(desc != nullptr); for (StreamParams& stream : desc->mutable_streams()) { for (unsigned int& ssrc : stream.ssrcs) { ++ssrc; @@ -2981,7 +2964,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, webrtc::MediaStream::Create(kStreams[1])); stream_1->AddTrack(stream_collection->at(0)->GetVideoTracks()[0]); stream_1->AddTrack(stream_collection->at(0)->GetAudioTracks()[0]); - pc_->AddStream(stream_1); + pc_->AddStream(stream_1.get()); ASSERT_TRUE(DoCreateOffer(&offer, nullptr)); EXPECT_TRUE(DoSetLocalDescription(std::move(offer))); @@ -3352,7 +3335,7 @@ TEST_P(PeerConnectionInterfaceTest, // First, create an offer with only a data channel and apply it as a remote // description. - pc_->CreateDataChannel("test", nullptr); + pc_->CreateDataChannelOrError("test", nullptr); std::unique_ptr offer; ASSERT_TRUE(DoCreateOffer(&offer, nullptr)); EXPECT_TRUE(DoSetRemoteDescription(std::move(offer))); @@ -3589,7 +3572,7 @@ TEST_F(PeerConnectionInterfaceTestPlanB, CreatePeerConnectionWithoutDtls(); rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(kStreamId1)); - pc_->AddStream(stream); + pc_->AddStream(stream.get()); rtc::scoped_refptr audio_track( CreateAudioTrack("audio_track")); rtc::scoped_refptr video_track( @@ -3662,7 +3645,7 @@ TEST_P(PeerConnectionInterfaceTest, ExtmapAllowMixedIsConfigurable) { INSTANTIATE_TEST_SUITE_P(PeerConnectionInterfaceTest, PeerConnectionInterfaceTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); class PeerConnectionMediaConfigTest : public ::testing::Test { @@ -3672,11 +3655,12 @@ class PeerConnectionMediaConfigTest : public ::testing::Test { } const cricket::MediaConfig TestCreatePeerConnection( const RTCConfiguration& config) { - rtc::scoped_refptr pc( - pcf_->CreatePeerConnection(config, nullptr, nullptr, &observer_)); - EXPECT_TRUE(pc.get()); - observer_.SetPeerConnectionInterface(pc.get()); - return pc->GetConfiguration().media_config; + PeerConnectionDependencies pc_dependencies(&observer_); + auto result = + pcf_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + EXPECT_TRUE(result.ok()); + observer_.SetPeerConnectionInterface(result.value().get()); + return result.value()->GetConfiguration().media_config; } rtc::scoped_refptr pcf_; @@ -3687,11 +3671,12 @@ class PeerConnectionMediaConfigTest : public ::testing::Test { TEST_F(PeerConnectionMediaConfigTest, TestCreateAndClose) { PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = SdpSemantics::kUnifiedPlan; - rtc::scoped_refptr pc( - pcf_->CreatePeerConnection(config, nullptr, nullptr, &observer_)); - EXPECT_TRUE(pc.get()); - observer_.SetPeerConnectionInterface(pc.get()); // Required. - pc->Close(); // No abort -> ok. + PeerConnectionDependencies pc_dependencies(&observer_); + auto result = + pcf_->CreatePeerConnectionOrError(config, std::move(pc_dependencies)); + EXPECT_TRUE(result.ok()); + observer_.SetPeerConnectionInterface(result.value().get()); + result.value()->Close(); // No abort -> ok. SUCCEED(); } @@ -3703,7 +3688,7 @@ TEST_F(PeerConnectionMediaConfigTest, TestDefaults) { const cricket::MediaConfig& media_config = TestCreatePeerConnection(config); - EXPECT_FALSE(media_config.enable_dscp); + EXPECT_TRUE(media_config.enable_dscp); EXPECT_TRUE(media_config.video.enable_cpu_adaptation); EXPECT_TRUE(media_config.video.enable_prerenderer_smoothing); EXPECT_FALSE(media_config.video.suspend_below_min_bitrate); diff --git a/pc/peer_connection_internal.h b/pc/peer_connection_internal.h index 16caade6c9..762f9b1e08 100644 --- a/pc/peer_connection_internal.h +++ b/pc/peer_connection_internal.h @@ -123,6 +123,8 @@ class PeerConnectionSdpMethods { virtual void TeardownDataChannelTransport_n() = 0; virtual void SetSctpDataMid(const std::string& mid) = 0; virtual void ResetSctpDataMid() = 0; + + virtual const FieldTrialsView& trials() const = 0; }; // Functions defined in this class are called by other objects, diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc index 66581ca852..851d9257e0 100644 --- a/pc/peer_connection_jsep_unittest.cc +++ b/pc/peer_connection_jsep_unittest.cc @@ -8,21 +8,56 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/call/call_factory_interface.h" +#include "api/field_trials_view.h" +#include "api/jsep.h" +#include "api/media_stream_interface.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" #include "api/transport/field_trial_based_config.h" +#include "api/transport/sctp_transport_factory_interface.h" +#include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "media/engine/webrtc_media_engine.h" #include "media/engine/webrtc_media_engine_defaults.h" +#include "modules/audio_device/include/audio_device.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" +#include "pc/channel_interface.h" #include "pc/media_session.h" -#include "pc/peer_connection_factory.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif #include "pc/test/fake_audio_capture_module.h" -#include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" #include "test/pc/sctp/fake_sctp_transport.h" @@ -82,15 +117,15 @@ class PeerConnectionJsepTest : public ::testing::Test { CreateModularPeerConnectionFactory( CreatePeerConnectionFactoryDependencies()); auto observer = std::make_unique(); - auto pc = pc_factory->CreatePeerConnection(config, nullptr, nullptr, - observer.get()); - if (!pc) { + auto result = pc_factory->CreatePeerConnectionOrError( + config, PeerConnectionDependencies(observer.get())); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); - return std::make_unique(pc_factory, pc, - std::move(observer)); + observer->SetPeerConnectionInterface(result.value().get()); + return std::make_unique( + pc_factory, result.MoveValue(), std::move(observer)); } std::unique_ptr vss_; @@ -1328,7 +1363,7 @@ TEST_F(PeerConnectionJsepTest, auto caller = CreatePeerConnection(); auto transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); - transceiver->sender()->SetTrack(caller->CreateAudioTrack(kTrackId)); + transceiver->sender()->SetTrack(caller->CreateAudioTrack(kTrackId).get()); auto offer = caller->CreateOffer(); auto contents = offer->description()->contents(); @@ -1605,7 +1640,7 @@ TEST_F(PeerConnectionJsepTest, AnswerBeforeOfferFails) { // two video tracks. TEST_F(PeerConnectionJsepTest, TwoVideoPlanBToUnifiedPlanFails) { RTCConfiguration config_planb; - config_planb.sdp_semantics = SdpSemantics::kPlanB; + config_planb.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED; auto caller = CreatePeerConnection(config_planb); auto callee = CreatePeerConnection(); caller->AddVideoTrack("video1"); @@ -1621,7 +1656,7 @@ TEST_F(PeerConnectionJsepTest, TwoVideoPlanBToUnifiedPlanFails) { TEST_F(PeerConnectionJsepTest, OneVideoUnifiedPlanToTwoVideoPlanBFails) { auto caller = CreatePeerConnection(); RTCConfiguration config_planb; - config_planb.sdp_semantics = SdpSemantics::kPlanB; + config_planb.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED; auto callee = CreatePeerConnection(config_planb); caller->AddVideoTrack("video"); callee->AddVideoTrack("video1"); @@ -1774,7 +1809,7 @@ TEST_F(PeerConnectionJsepTest, RollbackSupportedInUnifiedPlan) { TEST_F(PeerConnectionJsepTest, RollbackNotSupportedInPlanB) { RTCConfiguration config; - config.sdp_semantics = SdpSemantics::kPlanB; + config.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED; config.enable_implicit_rollback = true; auto caller = CreatePeerConnection(config); auto callee = CreatePeerConnection(config); @@ -1892,10 +1927,17 @@ TEST_F(PeerConnectionJsepTest, RollbackRemovesTransceiver) { caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); auto callee = CreatePeerConnection(); EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer())); - EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u); + ASSERT_EQ(callee->pc()->GetTransceivers().size(), 1u); + auto transceiver = callee->pc()->GetTransceivers()[0]; EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback())); EXPECT_EQ(callee->pc()->GetTransceivers().size(), 0u); EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u); + // The removed transceiver should be stopped and its receiver track should be + // ended. + EXPECT_TRUE(transceiver->stopping()); + EXPECT_TRUE(transceiver->stopping()); + EXPECT_EQ(transceiver->receiver()->track()->state(), + MediaStreamTrackInterface::kEnded); } TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) { @@ -1914,6 +1956,13 @@ TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) { EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer())); EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u); EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u); + // Because the transceiver is reusable, it must not be stopped and its + // receiver track must still be live. + auto transceiver = callee->pc()->GetTransceivers()[0]; + EXPECT_FALSE(transceiver->stopping()); + EXPECT_FALSE(transceiver->stopping()); + EXPECT_EQ(transceiver->receiver()->track()->state(), + MediaStreamTrackInterface::kLive); } TEST_F(PeerConnectionJsepTest, @@ -2184,6 +2233,60 @@ TEST_F(PeerConnectionJsepTest, RollbackRemoteDirectionChange) { EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u); } +TEST_F(PeerConnectionJsepTest, + RollbackRestoresFiredDirectionAndOnTrackCanFireAgain) { + auto caller = CreatePeerConnection(); + auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + auto callee = CreatePeerConnection(); + callee->AddAudioTrack("a"); + ASSERT_EQ(callee->pc()->GetTransceivers().size(), 1u); + auto callee_transceiver = callee->pc()->GetTransceivers()[0]; + EXPECT_FALSE(callee_transceiver->fired_direction().has_value()); + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + EXPECT_TRUE(callee_transceiver->fired_direction().has_value()); + EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u); + // The existing transceiver becomes associated. Because it already exists, + // rolling it back does not remove the transceiver, so if ontrack fires again + // later it will be because the transceiver's internal states were restored + // rather than due to the creation of a new transceiver. + EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u); + + // Rollback: the transceiver is no longer receiving. + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback())); + EXPECT_FALSE(callee_transceiver->fired_direction().has_value()); + EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u); + + // Set the remote offer again: ontrack should fire on the same transceiver. + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + EXPECT_TRUE(callee_transceiver->fired_direction().has_value()); + EXPECT_EQ(callee->observer()->add_track_events_.size(), 2u); + EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u); +} + +TEST_F(PeerConnectionJsepTest, + RollbackFromInactiveToReceivingMakesOnTrackFire) { + auto caller = CreatePeerConnection(); + auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + auto callee = CreatePeerConnection(); + // Perform full O/A so that transceiver is associated. Ontrack fires. + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u); + EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u); + ASSERT_TRUE( + caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal())); + + // Start negotiating to make the transceiver inactive. Onremovetrack fires. + caller_transceiver->SetDirectionWithError(RtpTransceiverDirection::kInactive); + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal())); + EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u); + EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u); + + // Rollback the inactivation. Ontrack should fire again. + EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback())); + EXPECT_EQ(callee->observer()->add_track_events_.size(), 2u); + EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u); +} + TEST_F(PeerConnectionJsepTest, RollbackAfterMultipleSLD) { auto callee = CreatePeerConnection(); callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); diff --git a/pc/peer_connection_media_unittest.cc b/pc/peer_connection_media_unittest.cc index 30034b4cd5..4973aed7b3 100644 --- a/pc/peer_connection_media_unittest.cc +++ b/pc/peer_connection_media_unittest.cc @@ -12,25 +12,56 @@ // PeerConnection and the underlying media engine, as well as tests that check // the media-related aspects of SDP. +#include +#include +#include +#include #include #include +#include #include +#include +#include +#include #include "absl/algorithm/container.h" #include "absl/types/optional.h" +#include "api/audio_options.h" #include "api/call/call_factory_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" +#include "media/base/codec.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_constants.h" +#include "media/base/media_engine.h" +#include "media/base/stream_params.h" #include "p2p/base/fake_port_allocator.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/transport_info.h" #include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" #include "pc/rtp_media_utils.h" -#include "pc/sdp_utils.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif -#include "pc/test/fake_rtc_certificate_generator.h" #include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" #include "test/gmock.h" @@ -110,13 +141,15 @@ class PeerConnectionMediaBaseTest : public ::testing::Test { auto observer = std::make_unique(); auto modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - auto pc = pc_factory->CreatePeerConnection(modified_config, - std::move(fake_port_allocator), - nullptr, observer.get()); - if (!pc) { + PeerConnectionDependencies pc_dependencies(observer.get()); + pc_dependencies.allocator = std::move(fake_port_allocator); + auto result = pc_factory->CreatePeerConnectionOrError( + modified_config, std::move(pc_dependencies)); + if (!result.ok()) { return nullptr; } + auto pc = result.MoveValue(); observer->SetPeerConnectionInterface(pc.get()); auto wrapper = std::make_unique( pc_factory, pc, std::move(observer)); @@ -195,7 +228,7 @@ class PeerConnectionMediaTestUnifiedPlan : public PeerConnectionMediaBaseTest { class PeerConnectionMediaTestPlanB : public PeerConnectionMediaBaseTest { protected: PeerConnectionMediaTestPlanB() - : PeerConnectionMediaBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionMediaBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; TEST_P(PeerConnectionMediaTest, @@ -663,7 +696,7 @@ INSTANTIATE_TEST_SUITE_P( PeerConnectionMediaTest, PeerConnectionMediaOfferDirectionTest, Combine( - Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_tuple(false, -1, RtpTransceiverDirection::kInactive), std::make_tuple(false, 0, RtpTransceiverDirection::kInactive), std::make_tuple(false, 1, RtpTransceiverDirection::kRecvOnly), @@ -777,7 +810,7 @@ TEST_P(PeerConnectionMediaAnswerDirectionTest, VerifyRejected) { INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest, PeerConnectionMediaAnswerDirectionTest, - Combine(Values(SdpSemantics::kPlanB, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(RtpTransceiverDirection::kInactive, RtpTransceiverDirection::kSendOnly, @@ -1018,7 +1051,7 @@ constexpr char kMLinesOutOfOrder[] = INSTANTIATE_TEST_SUITE_P( PeerConnectionMediaTest, PeerConnectionMediaInvalidMediaTest, - Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(std::make_tuple("remove video", RemoveVideoContent, kMLinesOutOfOrder), @@ -2212,7 +2245,7 @@ TEST_F(PeerConnectionMediaTestUnifiedPlan, INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaTest, PeerConnectionMediaTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); } // namespace webrtc diff --git a/pc/peer_connection_message_handler.cc b/pc/peer_connection_message_handler.cc index 54f75f00a9..1d3dcf8fe7 100644 --- a/pc/peer_connection_message_handler.cc +++ b/pc/peer_connection_message_handler.cc @@ -10,6 +10,7 @@ #include "pc/peer_connection_message_handler.h" +#include #include #include "api/jsep.h" @@ -116,7 +117,7 @@ void PeerConnectionMessageHandler::OnMessage(rtc::Message* msg) { case MSG_GETSTATS: { GetStatsMsg* param = static_cast(msg->pdata); StatsReports reports; - param->stats->GetStats(param->track, &reports); + param->stats->GetStats(param->track.get(), &reports); param->observer->OnComplete(reports); delete param; break; diff --git a/pc/peer_connection_rampup_tests.cc b/pc/peer_connection_rampup_tests.cc index 692ca9d689..e1ebebb8dd 100644 --- a/pc/peer_connection_rampup_tests.cc +++ b/pc/peer_connection_rampup_tests.cc @@ -15,8 +15,6 @@ #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/audio_options.h" @@ -24,14 +22,13 @@ #include "api/jsep.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/port_allocator.h" @@ -51,6 +48,7 @@ #include "rtc_base/location.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/socket_address.h" +#include "rtc_base/socket_factory.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/test_certificate_verifier.h" #include "rtc_base/thread.h" @@ -125,14 +123,15 @@ class PeerConnectionWrapperForRampUpTest : public PeerConnectionWrapper { video_track_sources_.back()->Start(); return rtc::scoped_refptr( pc_factory()->CreateVideoTrack(rtc::CreateRandomUuid(), - video_track_sources_.back())); + video_track_sources_.back().get())); } rtc::scoped_refptr CreateLocalAudioTrack( const cricket::AudioOptions options) { rtc::scoped_refptr source = pc_factory()->CreateAudioSource(options); - return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(), source); + return pc_factory()->CreateAudioTrack(rtc::CreateRandomUuid(), + source.get()); } private: diff --git a/pc/peer_connection_rtp_unittest.cc b/pc/peer_connection_rtp_unittest.cc index fac738b7ba..80a4a46f7f 100644 --- a/pc/peer_connection_rtp_unittest.cc +++ b/pc/peer_connection_rtp_unittest.cc @@ -8,8 +8,9 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include #include #include #include @@ -17,8 +18,6 @@ #include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" @@ -30,14 +29,13 @@ #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" #include "api/set_remote_description_observer_interface.h" #include "api/uma_metrics.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" #include "media/base/stream_params.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" @@ -71,8 +69,7 @@ using ::testing::Values; const uint32_t kDefaultTimeout = 10000u; template -class OnSuccessObserver : public rtc::RefCountedObject< - webrtc::SetRemoteDescriptionObserverInterface> { +class OnSuccessObserver : public webrtc::SetRemoteDescriptionObserverInterface { public: explicit OnSuccessObserver(MethodFunctor on_success) : on_success_(std::move(on_success)) {} @@ -111,7 +108,7 @@ class PeerConnectionRtpBaseTest : public ::testing::Test { std::unique_ptr CreatePeerConnectionWithPlanB() { RTCConfiguration config; - config.sdp_semantics = SdpSemantics::kPlanB; + config.sdp_semantics = SdpSemantics::kPlanB_DEPRECATED; return CreatePeerConnectionInternal(config); } @@ -138,12 +135,12 @@ class PeerConnectionRtpBaseTest : public ::testing::Test { std::unique_ptr CreatePeerConnectionInternal( const RTCConfiguration& config) { auto observer = std::make_unique(); - auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr, - observer.get()); - EXPECT_TRUE(pc.get()); - observer->SetPeerConnectionInterface(pc.get()); - return std::make_unique(pc_factory_, pc, - std::move(observer)); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, PeerConnectionDependencies(observer.get())); + EXPECT_TRUE(result.ok()); + observer->SetPeerConnectionInterface(result.value().get()); + return std::make_unique( + pc_factory_, result.MoveValue(), std::move(observer)); } }; @@ -157,7 +154,7 @@ class PeerConnectionRtpTest class PeerConnectionRtpTestPlanB : public PeerConnectionRtpBaseTest { protected: PeerConnectionRtpTestPlanB() - : PeerConnectionRtpBaseTest(SdpSemantics::kPlanB) {} + : PeerConnectionRtpBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} }; class PeerConnectionRtpTestUnifiedPlan : public PeerConnectionRtpBaseTest { @@ -202,7 +199,7 @@ TEST_P(PeerConnectionRtpTest, AddTrackWithoutStreamFiresOnAddTrack) { const auto& add_track_event = callee->observer()->add_track_events_[0]; EXPECT_EQ(add_track_event.streams, add_track_event.receiver->streams()); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // Since we are not supporting the no stream case with Plan B, there should // be a generated stream, even though we didn't set one with AddTrack. ASSERT_EQ(1u, add_track_event.streams.size()); @@ -545,7 +542,7 @@ TEST_P(PeerConnectionRtpTest, AddTrackWithoutStreamAddsReceiver) { auto receiver_added = callee->pc()->GetReceivers()[0]; EXPECT_EQ("audio_track", receiver_added->track()->id()); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // Since we are not supporting the no stream case with Plan B, there should // be a generated stream, even though we didn't set one with AddTrack. ASSERT_EQ(1u, receiver_added->streams().size()); @@ -924,7 +921,7 @@ TEST_P(PeerConnectionRtpTest, rtc::make_ref_counted(); auto offer = caller->CreateOfferAndSetAsLocal(); - callee->pc()->SetRemoteDescription(observer, offer.release()); + callee->pc()->SetRemoteDescription(observer.get(), offer.release()); callee = nullptr; rtc::Thread::Current()->ProcessMessages(0); EXPECT_FALSE(observer->called()); @@ -1992,9 +1989,9 @@ TEST_P(PeerConnectionRtpTest, CreateTwoSendersWithSameTrack) { EXPECT_TRUE(sender1->SetTrack(nullptr)); auto sender2 = caller->AddTrack(track); EXPECT_TRUE(sender2); - EXPECT_TRUE(sender1->SetTrack(track)); + EXPECT_TRUE(sender1->SetTrack(track.get())); - if (sdp_semantics_ == SdpSemantics::kPlanB) { + if (sdp_semantics_ == SdpSemantics::kPlanB_DEPRECATED) { // TODO(hbos): When https://crbug.com/webrtc/8734 is resolved, this should // return true, and doing `callee->SetRemoteDescription()` should work. EXPECT_FALSE(caller->CreateOfferAndSetAsLocal()); @@ -2036,7 +2033,7 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, INSTANTIATE_TEST_SUITE_P(PeerConnectionRtpTest, PeerConnectionRtpTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); } // namespace webrtc diff --git a/pc/peer_connection_sdp_methods.h b/pc/peer_connection_sdp_methods.h new file mode 100644 index 0000000000..972ad9c7b4 --- /dev/null +++ b/pc/peer_connection_sdp_methods.h @@ -0,0 +1,131 @@ +/* + * Copyright 2022 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 PC_PEER_CONNECTION_SDP_METHODS_H_ +#define PC_PEER_CONNECTION_SDP_METHODS_H_ + +#include +#include +#include +#include +#include + +#include "api/peer_connection_interface.h" +#include "pc/jsep_transport_controller.h" +#include "pc/peer_connection_message_handler.h" +#include "pc/sctp_data_channel.h" +#include "pc/usage_pattern.h" + +namespace webrtc { + +class DataChannelController; +class RtpTransmissionManager; +class StatsCollector; + +// This interface defines the functions that are needed for +// SdpOfferAnswerHandler to access PeerConnection internal state. +class PeerConnectionSdpMethods { + public: + virtual ~PeerConnectionSdpMethods() = default; + + // The SDP session ID as defined by RFC 3264. + virtual std::string session_id() const = 0; + + // Returns true if the ICE restart flag above was set, and no ICE restart has + // occurred yet for this transport (by applying a local description with + // changed ufrag/password). If the transport has been deleted as a result of + // bundling, returns false. + virtual bool NeedsIceRestart(const std::string& content_name) const = 0; + + virtual absl::optional sctp_mid() const = 0; + + // Functions below this comment are known to only be accessed + // from SdpOfferAnswerHandler. + // Return a pointer to the active configuration. + virtual const PeerConnectionInterface::RTCConfiguration* configuration() + const = 0; + + // Report the UMA metric SdpFormatReceived for the given remote description. + virtual void ReportSdpFormatReceived( + const SessionDescriptionInterface& remote_description) = 0; + + // Report the UMA metric BundleUsage for the given remote description. + virtual void ReportSdpBundleUsage( + const SessionDescriptionInterface& remote_description) = 0; + + virtual PeerConnectionMessageHandler* message_handler() = 0; + virtual RtpTransmissionManager* rtp_manager() = 0; + virtual const RtpTransmissionManager* rtp_manager() const = 0; + virtual bool dtls_enabled() const = 0; + virtual const PeerConnectionFactoryInterface::Options* options() const = 0; + + // Returns the CryptoOptions for this PeerConnection. This will always + // return the RTCConfiguration.crypto_options if set and will only default + // back to the PeerConnectionFactory settings if nothing was set. + virtual CryptoOptions GetCryptoOptions() = 0; + virtual JsepTransportController* transport_controller_s() = 0; + virtual JsepTransportController* transport_controller_n() = 0; + virtual DataChannelController* data_channel_controller() = 0; + virtual cricket::PortAllocator* port_allocator() = 0; + virtual StatsCollector* stats() = 0; + // Returns the observer. Will crash on CHECK if the observer is removed. + virtual PeerConnectionObserver* Observer() const = 0; + virtual bool GetSctpSslRole(rtc::SSLRole* role) = 0; + virtual PeerConnectionInterface::IceConnectionState + ice_connection_state_internal() = 0; + virtual void SetIceConnectionState( + PeerConnectionInterface::IceConnectionState new_state) = 0; + virtual void NoteUsageEvent(UsageEvent event) = 0; + virtual bool IsClosed() const = 0; + // Returns true if the PeerConnection is configured to use Unified Plan + // semantics for creating offers/answers and setting local/remote + // descriptions. If this is true the RtpTransceiver API will also be available + // to the user. If this is false, Plan B semantics are assumed. + // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once + // sufficient time has passed. + virtual bool IsUnifiedPlan() const = 0; + virtual bool ValidateBundleSettings( + const cricket::SessionDescription* desc, + const std::map& + bundle_groups_by_mid) = 0; + + virtual absl::optional GetDataMid() const = 0; + // Internal implementation for AddTransceiver family of methods. If + // `fire_callback` is set, fires OnRenegotiationNeeded callback if successful. + virtual RTCErrorOr> + AddTransceiver(cricket::MediaType media_type, + rtc::scoped_refptr track, + const RtpTransceiverInit& init, + bool fire_callback = true) = 0; + // Asynchronously calls SctpTransport::Start() on the network thread for + // `sctp_mid()` if set. Called as part of setting the local description. + virtual void StartSctpTransport(int local_port, + int remote_port, + int max_message_size) = 0; + + // Asynchronously adds a remote candidate on the network thread. + virtual void AddRemoteCandidate(const std::string& mid, + const cricket::Candidate& candidate) = 0; + + virtual Call* call_ptr() = 0; + // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by + // this session. + virtual bool SrtpRequired() const = 0; + virtual bool SetupDataChannelTransport_n(const std::string& mid) = 0; + virtual void TeardownDataChannelTransport_n() = 0; + virtual void SetSctpDataMid(const std::string& mid) = 0; + virtual void ResetSctpDataMid() = 0; + + virtual const FieldTrialsView& trials() const = 0; +}; + +} // namespace webrtc + +#endif // PC_PEER_CONNECTION_SDP_METHODS_H_ diff --git a/pc/peer_connection_signaling_unittest.cc b/pc/peer_connection_signaling_unittest.cc index 90dd868de6..54c49af1a8 100644 --- a/pc/peer_connection_signaling_unittest.cc +++ b/pc/peer_connection_signaling_unittest.cc @@ -12,20 +12,52 @@ // machine, as well as tests that check basic, media-agnostic aspects of SDP. #include +#include +#include +#include #include +#include +#include #include +#include +#include +#include +#include "absl/types/optional.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" -#include "api/jsep_session_description.h" +#include "api/dtls_transport_interface.h" +#include "api/jsep.h" +#include "api/media_types.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" +#include "media/base/codec.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "p2p/base/port_allocator.h" #include "pc/peer_connection.h" #include "pc/peer_connection_proxy.h" #include "pc/peer_connection_wrapper.h" #include "pc/sdp_utils.h" -#include "pc/webrtc_sdp.h" +#include "pc/session_description.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/thread.h" +#include "test/gtest.h" #ifdef WEBRTC_ANDROID #include "pc/test/android_test_initializer.h" #endif @@ -33,7 +65,6 @@ #include "pc/test/fake_rtc_certificate_generator.h" #include "rtc_base/gunit.h" #include "rtc_base/virtual_socket_server.h" -#include "test/gmock.h" namespace webrtc { @@ -116,15 +147,15 @@ class PeerConnectionSignalingBaseTest : public ::testing::Test { auto observer = std::make_unique(); RTCConfiguration modified_config = config; modified_config.sdp_semantics = sdp_semantics_; - auto pc = pc_factory_->CreatePeerConnection(modified_config, nullptr, - nullptr, observer.get()); - if (!pc) { + auto result = pc_factory_->CreatePeerConnectionOrError( + modified_config, PeerConnectionDependencies(observer.get())); + if (!result.ok()) { return nullptr; } - observer->SetPeerConnectionInterface(pc.get()); + observer->SetPeerConnectionInterface(result.value().get()); return std::make_unique( - pc_factory_, pc, std::move(observer)); + pc_factory_, result.MoveValue(), std::move(observer)); } // Accepts the same arguments as CreatePeerConnection and adds default audio @@ -437,7 +468,7 @@ TEST_P(PeerConnectionSignalingStateTest, SetRemoteAnswer) { INSTANTIATE_TEST_SUITE_P(PeerConnectionSignalingTest, PeerConnectionSignalingStateTest, - Combine(Values(SdpSemantics::kPlanB, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), Values(SignalingState::kStable, SignalingState::kHaveLocalOffer, @@ -540,7 +571,7 @@ TEST_P(PeerConnectionSignalingTest, CreateOffersAndShutdown) { rtc::scoped_refptr observers[100]; for (auto& observer : observers) { observer = rtc::make_ref_counted(); - caller->pc()->CreateOffer(observer, options); + caller->pc()->CreateOffer(observer.get(), options); } // Destroy the PeerConnection. @@ -562,7 +593,7 @@ TEST_P(PeerConnectionSignalingTest, CloseCreateOfferAndShutdown) { auto caller = CreatePeerConnection(); auto observer = rtc::make_ref_counted(); caller->pc()->Close(); - caller->pc()->CreateOffer(observer, RTCOfferAnswerOptions()); + caller->pc()->CreateOffer(observer.get(), RTCOfferAnswerOptions()); caller.reset(nullptr); EXPECT_TRUE(observer->called()); } @@ -579,8 +610,7 @@ TEST_P(PeerConnectionSignalingTest, TEST_P(PeerConnectionSignalingTest, ImplicitCreateOfferAndShutdown) { auto caller = CreatePeerConnection(); - rtc::scoped_refptr observer( - new FakeSetLocalDescriptionObserver()); + auto observer = rtc::make_ref_counted(); caller->pc()->SetLocalDescription(observer); caller.reset(nullptr); // The new observer gets invoked because it is called immediately. @@ -601,8 +631,7 @@ TEST_P(PeerConnectionSignalingTest, TEST_P(PeerConnectionSignalingTest, CloseBeforeImplicitCreateOfferAndShutdown) { auto caller = CreatePeerConnection(); - rtc::scoped_refptr observer( - new FakeSetLocalDescriptionObserver()); + auto observer = rtc::make_ref_counted(); caller->pc()->Close(); caller->pc()->SetLocalDescription(observer); caller.reset(nullptr); @@ -624,8 +653,7 @@ TEST_P(PeerConnectionSignalingTest, TEST_P(PeerConnectionSignalingTest, CloseAfterImplicitCreateOfferAndShutdown) { auto caller = CreatePeerConnection(); - rtc::scoped_refptr observer( - new FakeSetLocalDescriptionObserver()); + auto observer = rtc::make_ref_counted(); caller->pc()->SetLocalDescription(observer); caller->pc()->Close(); caller.reset(nullptr); @@ -639,8 +667,7 @@ TEST_P(PeerConnectionSignalingTest, auto caller = CreatePeerConnection(); auto offer = caller->CreateOffer(RTCOfferAnswerOptions()); - rtc::scoped_refptr observer( - new FakeSetLocalDescriptionObserver()); + auto observer = rtc::make_ref_counted(); caller->pc()->SetLocalDescription(std::move(offer), observer); // The new observer is invoked immediately. EXPECT_TRUE(observer->called()); @@ -653,7 +680,7 @@ TEST_P(PeerConnectionSignalingTest, auto offer = caller->CreateOffer(RTCOfferAnswerOptions()); auto observer = MockSetSessionDescriptionObserver::Create(); - caller->pc()->SetLocalDescription(observer, offer.release()); + caller->pc()->SetLocalDescription(observer.get(), offer.release()); // The old observer is not invoked immediately. EXPECT_FALSE(observer->called()); // Process all currently pending messages by waiting for a posted task to run. @@ -693,7 +720,7 @@ TEST_P(PeerConnectionSignalingTest, CreateOfferBlocksSetRemoteDescription) { // Synchronously invoke CreateOffer() and SetRemoteDescription(). The // SetRemoteDescription() operation should be chained to be executed // asynchronously, when CreateOffer() completes. - callee->pc()->CreateOffer(offer_observer, RTCOfferAnswerOptions()); + callee->pc()->CreateOffer(offer_observer.get(), RTCOfferAnswerOptions()); callee->pc()->SetRemoteDescription( std::move(offer), rtc::make_ref_counted()); @@ -775,7 +802,7 @@ TEST_P(PeerConnectionSignalingTest, auto callee_set_remote_description_observer = MockSetSessionDescriptionObserver::Create(); callee->pc()->SetRemoteDescription( - callee_set_remote_description_observer, + callee_set_remote_description_observer.get(), CloneSessionDescription(caller->pc()->pending_local_description()) .release()); @@ -795,7 +822,7 @@ TEST_P(PeerConnectionSignalingTest, auto caller_set_remote_description_observer = MockSetSessionDescriptionObserver::Create(); caller->pc()->SetRemoteDescription( - caller_set_remote_description_observer, + caller_set_remote_description_observer.get(), CloneSessionDescription(callee->pc()->current_local_description()) .release()); EXPECT_TRUE_WAIT(caller_set_remote_description_observer->called(), @@ -1073,7 +1100,7 @@ TEST_P(PeerConnectionSignalingTest, MidAttributeMaxLength) { INSTANTIATE_TEST_SUITE_P(PeerConnectionSignalingTest, PeerConnectionSignalingTest, - Values(SdpSemantics::kPlanB, + Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan)); class PeerConnectionSignalingUnifiedPlanTest @@ -1099,7 +1126,7 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, // waiting for it would not ensure synchronicity. RTC_DCHECK(!caller->pc()->GetTransceivers()[0]->mid().has_value()); caller->pc()->SetLocalDescription( - rtc::make_ref_counted(), + rtc::make_ref_counted().get(), offer.release()); EXPECT_TRUE(caller->pc()->GetTransceivers()[0]->mid().has_value()); } @@ -1135,11 +1162,12 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, // operation executed immediately. RTC_DCHECK(!pc->GetTransceivers()[0]->mid().has_value()); pc->SetLocalDescription( - rtc::make_ref_counted(), + rtc::make_ref_counted() + .get(), desc); EXPECT_TRUE(pc->GetTransceivers()[0]->mid().has_value()); }); - caller->pc()->CreateOffer(offer_observer, RTCOfferAnswerOptions()); + caller->pc()->CreateOffer(offer_observer.get(), RTCOfferAnswerOptions()); EXPECT_TRUE_WAIT(offer_observer->was_called(), kWaitTimeout); } @@ -1226,7 +1254,7 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, EXPECT_TRUE(caller->observer()->has_negotiation_needed_event()); auto observer = rtc::make_ref_counted(); - caller->pc()->CreateOffer(observer, RTCOfferAnswerOptions()); + caller->pc()->CreateOffer(observer.get(), RTCOfferAnswerOptions()); // For this test to work, the operation has to be pending, i.e. the observer // has not yet been invoked. EXPECT_FALSE(observer->called()); @@ -1308,7 +1336,7 @@ TEST_F(PeerConnectionSignalingUnifiedPlanTest, RtxReofferApt) { EXPECT_TRUE( callee->SetLocalDescription(CloneSessionDescription(answer.get()))); - callee->pc()->GetTransceivers()[0]->Stop(); + callee->pc()->GetTransceivers()[0]->StopStandard(); auto reoffer = callee->CreateOffer(RTCOfferAnswerOptions()); auto codecs = reoffer->description() ->contents()[0] diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index 31385754b7..e704aeba5a 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -8,26 +8,51 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include +#include +#include #include #include // no-presubmit-check TODO(webrtc:8982) +#include +#include +#include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "api/audio/audio_mixer.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" +#include "api/jsep.h" #include "api/media_types.h" +#include "api/peer_connection_interface.h" #include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" #include "api/uma_metrics.h" +#include "api/video/video_codec_constants.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "pc/peer_connection.h" +#include "media/base/rid_description.h" +#include "media/base/stream_params.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "pc/channel_interface.h" #include "pc/peer_connection_wrapper.h" +#include "pc/session_description.h" +#include "pc/simulcast_description.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/mock_peer_connection_observers.h" -#include "rtc_base/gunit.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" +#include "rtc_base/thread.h" +#include "rtc_base/unique_id_generator.h" #include "system_wrappers/include/metrics.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::Contains; using ::testing::Each; @@ -112,10 +137,11 @@ class PeerConnectionSimulcastTests : public ::testing::Test { PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = SdpSemantics::kUnifiedPlan; PeerConnectionDependencies pcd(observer); - auto pc = pc_factory_->CreatePeerConnection(config, std::move(pcd)); - EXPECT_TRUE(pc); - observer->SetPeerConnectionInterface(pc); - return pc; + auto result = + pc_factory_->CreatePeerConnectionOrError(config, std::move(pcd)); + EXPECT_TRUE(result.ok()); + observer->SetPeerConnectionInterface(result.value().get()); + return result.MoveValue(); } std::unique_ptr CreatePeerConnectionWrapper() { diff --git a/pc/peer_connection_wrapper.cc b/pc/peer_connection_wrapper.cc index 641d8bf053..e23b0f5f2d 100644 --- a/pc/peer_connection_wrapper.cc +++ b/pc/peer_connection_wrapper.cc @@ -12,8 +12,6 @@ #include -#include -#include #include #include @@ -137,7 +135,7 @@ std::unique_ptr PeerConnectionWrapper::CreateSdp( rtc::FunctionView fn, std::string* error_out) { auto observer = rtc::make_ref_counted(); - fn(observer); + fn(observer.get()); EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout); if (error_out && !observer->result()) { *error_out = observer->error(); @@ -181,7 +179,7 @@ bool PeerConnectionWrapper::SetSdp( rtc::FunctionView fn, std::string* error_out) { auto observer = rtc::make_ref_counted(); - fn(observer); + fn(observer.get()); EXPECT_EQ_WAIT(true, observer->called(), kDefaultTimeout); if (error_out && !observer->result()) { *error_out = observer->error(); @@ -279,7 +277,8 @@ rtc::scoped_refptr PeerConnectionWrapper::CreateAudioTrack( rtc::scoped_refptr PeerConnectionWrapper::CreateVideoTrack( const std::string& label) { - return pc_factory()->CreateVideoTrack(label, FakeVideoTrackSource::Create()); + return pc_factory()->CreateVideoTrack(label, + FakeVideoTrackSource::Create().get()); } rtc::scoped_refptr PeerConnectionWrapper::AddTrack( @@ -331,7 +330,7 @@ bool PeerConnectionWrapper::IsIceConnected() { rtc::scoped_refptr PeerConnectionWrapper::GetStats() { auto callback = rtc::make_ref_counted(); - pc()->GetStats(callback); + pc()->GetStats(callback.get()); EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout); return callback->report(); } diff --git a/pc/proxy.h b/pc/proxy.h index 85cb70d34c..5b164929b7 100644 --- a/pc/proxy.h +++ b/pc/proxy.h @@ -53,12 +53,12 @@ // 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. #ifndef PC_PROXY_H_ #define PC_PROXY_H_ +#include + #include #include #include @@ -69,6 +69,7 @@ #include "api/task_queue/queued_task.h" #include "api/task_queue/task_queue_base.h" #include "rtc_base/event.h" +#include "rtc_base/location.h" #include "rtc_base/message_handler.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/string_utils.h" @@ -93,7 +94,7 @@ class ScopedTrace { ~ScopedTrace(); private: - const char* const class_and_method_name_; + [[maybe_unused]] const char* const class_and_method_name_; }; } // namespace proxy_internal @@ -226,26 +227,26 @@ class ConstMethodCall : public QueuedTask { constexpr char class_name##ProxyWithInternal::proxy_name_[]; // clang-format on -#define PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ - protected: \ - class_name##ProxyWithInternal(rtc::Thread* primary_thread, \ - INTERNAL_CLASS* c) \ - : primary_thread_(primary_thread), c_(c) {} \ - \ - private: \ +#define PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ + protected: \ + class_name##ProxyWithInternal(rtc::Thread* primary_thread, \ + rtc::scoped_refptr c) \ + : primary_thread_(primary_thread), c_(std::move(c)) {} \ + \ + private: \ mutable rtc::Thread* primary_thread_; -#define SECONDARY_PROXY_MAP_BOILERPLATE(class_name) \ - protected: \ - class_name##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* primary_thread_; \ +#define SECONDARY_PROXY_MAP_BOILERPLATE(class_name) \ + protected: \ + class_name##ProxyWithInternal(rtc::Thread* primary_thread, \ + rtc::Thread* secondary_thread, \ + rtc::scoped_refptr c) \ + : primary_thread_(primary_thread), \ + secondary_thread_(secondary_thread), \ + c_(std::move(c)) {} \ + \ + private: \ + mutable rtc::Thread* primary_thread_; \ mutable rtc::Thread* secondary_thread_; // Note that the destructor is protected so that the proxy can only be @@ -283,15 +284,15 @@ class ConstMethodCall : public QueuedTask { void DestroyInternal() { delete c_; } \ INTERNAL_CLASS* c_; -#define BEGIN_PRIMARY_PROXY_MAP(class_name) \ - PROXY_MAP_BOILERPLATE(class_name) \ - PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ - REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name) \ - public: \ - static rtc::scoped_refptr Create( \ - rtc::Thread* primary_thread, INTERNAL_CLASS* c) { \ - return rtc::make_ref_counted( \ - primary_thread, c); \ +#define BEGIN_PRIMARY_PROXY_MAP(class_name) \ + PROXY_MAP_BOILERPLATE(class_name) \ + PRIMARY_PROXY_MAP_BOILERPLATE(class_name) \ + REFCOUNTED_PROXY_MAP_BOILERPLATE(class_name) \ + public: \ + static rtc::scoped_refptr Create( \ + rtc::Thread* primary_thread, rtc::scoped_refptr c) { \ + return rtc::make_ref_counted( \ + primary_thread, std::move(c)); \ } #define BEGIN_PROXY_MAP(class_name) \ @@ -301,22 +302,9 @@ class ConstMethodCall : public QueuedTask { public: \ static rtc::scoped_refptr Create( \ rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \ - INTERNAL_CLASS* c) { \ + rtc::scoped_refptr c) { \ return rtc::make_ref_counted( \ - primary_thread, secondary_thread, c); \ - } - -#define BEGIN_OWNED_PROXY_MAP(class_name) \ - PROXY_MAP_BOILERPLATE(class_name) \ - SECONDARY_PROXY_MAP_BOILERPLATE(class_name) \ - OWNED_PROXY_MAP_BOILERPLATE(class_name) \ - public: \ - static std::unique_ptr Create( \ - rtc::Thread* primary_thread, rtc::Thread* secondary_thread, \ - std::unique_ptr c) { \ - return std::unique_ptr( \ - new class_name##ProxyWithInternal(primary_thread, secondary_thread, \ - c.release())); \ + primary_thread, secondary_thread, std::move(c)); \ } #define PROXY_PRIMARY_THREAD_DESTRUCTOR() \ diff --git a/pc/proxy_unittest.cc b/pc/proxy_unittest.cc index ef3d97eddc..48c087f690 100644 --- a/pc/proxy_unittest.cc +++ b/pc/proxy_unittest.cc @@ -98,7 +98,7 @@ class SignalingProxyTest : public ::testing::Test { ASSERT_TRUE(signaling_thread_->Start()); fake_ = Fake::Create(); fake_signaling_proxy_ = - FakeSignalingProxy::Create(signaling_thread_.get(), fake_.get()); + FakeSignalingProxy::Create(signaling_thread_.get(), fake_); } protected: @@ -186,8 +186,8 @@ class ProxyTest : public ::testing::Test { ASSERT_TRUE(signaling_thread_->Start()); ASSERT_TRUE(worker_thread_->Start()); fake_ = Fake::Create(); - fake_proxy_ = FakeProxy::Create(signaling_thread_.get(), - worker_thread_.get(), fake_.get()); + fake_proxy_ = + FakeProxy::Create(signaling_thread_.get(), worker_thread_.get(), fake_); } protected: @@ -256,54 +256,4 @@ TEST_F(ProxyTest, WorkerMethod2) { EXPECT_EQ("Method2", fake_proxy_->Method2(arg1, arg2)); } -// Interface for testing OWNED_PROXY_MAP. -class FooInterface { - public: - virtual ~FooInterface() {} - virtual void Bar() = 0; -}; - -class Foo : public FooInterface { - public: - Foo() {} - MOCK_METHOD(void, Bar, (), (override)); -}; - -BEGIN_OWNED_PROXY_MAP(Foo) -PROXY_PRIMARY_THREAD_DESTRUCTOR() -PROXY_METHOD0(void, Bar) -END_PROXY_MAP(Foo) - -class OwnedProxyTest : public ::testing::Test { - public: - OwnedProxyTest() - : signaling_thread_(rtc::Thread::Create()), - worker_thread_(rtc::Thread::Create()), - foo_(new Foo()), - foo_proxy_(FooProxy::Create(signaling_thread_.get(), - worker_thread_.get(), - std::unique_ptr(foo_))) { - signaling_thread_->Start(); - worker_thread_->Start(); - } - - void CheckSignalingThread() { EXPECT_TRUE(signaling_thread_->IsCurrent()); } - void CheckWorkerThread() { EXPECT_TRUE(worker_thread_->IsCurrent()); } - - protected: - std::unique_ptr signaling_thread_; - std::unique_ptr worker_thread_; - Foo* foo_; // Owned by foo_proxy_, not this class. - std::unique_ptr foo_proxy_; -}; - -// Just tests that a method can be invoked using an "owned proxy" (as opposed -// to normal ref-counted version). -TEST_F(OwnedProxyTest, BasicTest) { - EXPECT_CALL(*foo_, Bar()) - .Times(Exactly(1)) - .WillOnce(InvokeWithoutArgs(this, &OwnedProxyTest::CheckSignalingThread)); - foo_proxy_->Bar(); -} - } // namespace webrtc diff --git a/pc/remote_audio_source.cc b/pc/remote_audio_source.cc index 78a35f32a8..781e4512be 100644 --- a/pc/remote_audio_source.cc +++ b/pc/remote_audio_source.cc @@ -13,6 +13,7 @@ #include #include +#include #include "absl/algorithm/container.h" #include "api/scoped_refptr.h" diff --git a/pc/remote_audio_source.h b/pc/remote_audio_source.h index 2eae073272..89af4db714 100644 --- a/pc/remote_audio_source.h +++ b/pc/remote_audio_source.h @@ -21,7 +21,6 @@ #include "api/media_stream_interface.h" #include "api/notifier.h" #include "media/base/media_channel.h" -#include "pc/channel.h" #include "rtc_base/message_handler.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread.h" diff --git a/pc/rtc_stats_collector.cc b/pc/rtc_stats_collector.cc index 7e9807e449..916d4a7a4c 100644 --- a/pc/rtc_stats_collector.cc +++ b/pc/rtc_stats_collector.cc @@ -10,26 +10,28 @@ #include "pc/rtc_stats_collector.h" +#include #include -#include #include #include #include #include +#include #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/candidate.h" +#include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" #include "api/rtp_parameters.h" -#include "api/rtp_receiver_interface.h" -#include "api/rtp_sender_interface.h" #include "api/sequence_checker.h" #include "api/stats/rtc_stats.h" #include "api/stats/rtcstats_objects.h" #include "api/task_queue/queued_task.h" +#include "api/units/time_delta.h" #include "api/video/video_content_type.h" #include "common_video/include/quality_limitation_reason.h" #include "media/base/media_channel.h" @@ -37,14 +39,14 @@ #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "p2p/base/connection_info.h" -#include "p2p/base/dtls_transport_internal.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" -#include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/data_channel_utils.h" #include "pc/rtc_stats_traversal.h" +#include "pc/rtp_receiver_proxy.h" +#include "pc/rtp_sender_proxy.h" #include "pc/webrtc_sdp.h" #include "rtc_base/checks.h" #include "rtc_base/ip_address.h" @@ -208,6 +210,20 @@ const char* IceCandidatePairStateToRTCStatsIceCandidatePairState( } } +const char* IceRoleToRTCIceRole(cricket::IceRole role) { + switch (role) { + case cricket::IceRole::ICEROLE_UNKNOWN: + return RTCIceRole::kUnknown; + case cricket::IceRole::ICEROLE_CONTROLLED: + return RTCIceRole::kControlled; + case cricket::IceRole::ICEROLE_CONTROLLING: + return RTCIceRole::kControlling; + default: + RTC_DCHECK_NOTREACHED(); + return nullptr; + } +} + const char* DtlsTransportStateToRTCDtlsTransportState( DtlsTransportState state) { switch (state) { @@ -227,7 +243,29 @@ const char* DtlsTransportStateToRTCDtlsTransportState( } } -const char* NetworkAdapterTypeToStatsType(rtc::AdapterType type) { +const char* IceTransportStateToRTCIceTransportState(IceTransportState state) { + switch (state) { + case IceTransportState::kNew: + return RTCIceTransportState::kNew; + case IceTransportState::kChecking: + return RTCIceTransportState::kChecking; + case IceTransportState::kConnected: + return RTCIceTransportState::kConnected; + case IceTransportState::kCompleted: + return RTCIceTransportState::kCompleted; + case IceTransportState::kFailed: + return RTCIceTransportState::kFailed; + case IceTransportState::kDisconnected: + return RTCIceTransportState::kDisconnected; + case IceTransportState::kClosed: + return RTCIceTransportState::kClosed; + default: + RTC_CHECK_NOTREACHED(); + return nullptr; + } +} + +const char* NetworkTypeToStatsType(rtc::AdapterType type) { switch (type) { case rtc::ADAPTER_TYPE_CELLULAR: case rtc::ADAPTER_TYPE_CELLULAR_2G: @@ -250,6 +288,36 @@ const char* NetworkAdapterTypeToStatsType(rtc::AdapterType type) { return nullptr; } +absl::string_view NetworkTypeToStatsNetworkAdapterType(rtc::AdapterType type) { + switch (type) { + case rtc::ADAPTER_TYPE_CELLULAR: + return RTCNetworkAdapterType::kCellular; + case rtc::ADAPTER_TYPE_CELLULAR_2G: + return RTCNetworkAdapterType::kCellular2g; + case rtc::ADAPTER_TYPE_CELLULAR_3G: + return RTCNetworkAdapterType::kCellular3g; + case rtc::ADAPTER_TYPE_CELLULAR_4G: + return RTCNetworkAdapterType::kCellular4g; + case rtc::ADAPTER_TYPE_CELLULAR_5G: + return RTCNetworkAdapterType::kCellular5g; + case rtc::ADAPTER_TYPE_ETHERNET: + return RTCNetworkAdapterType::kEthernet; + case rtc::ADAPTER_TYPE_WIFI: + return RTCNetworkAdapterType::kWifi; + case rtc::ADAPTER_TYPE_UNKNOWN: + return RTCNetworkAdapterType::kUnknown; + case rtc::ADAPTER_TYPE_LOOPBACK: + return RTCNetworkAdapterType::kLoopback; + case rtc::ADAPTER_TYPE_ANY: + return RTCNetworkAdapterType::kAny; + case rtc::ADAPTER_TYPE_VPN: + /* should not be handled here. Vpn is modelled as a bool */ + break; + } + RTC_DCHECK_NOTREACHED(); + return {}; +} + const char* QualityLimitationReasonToRTCQualityLimitationReason( QualityLimitationReason reason) { switch (reason) { @@ -489,6 +557,9 @@ void SetInboundRTPStreamStatsFromVideoReceiverInfo( inbound_video->total_decode_time = static_cast(video_receiver_info.total_decode_time_ms) / rtc::kNumMillisecsPerSec; + inbound_video->total_processing_delay = + static_cast(video_receiver_info.total_processing_delay.ms()) / + rtc::kNumMillisecsPerSec; inbound_video->total_inter_frame_delay = video_receiver_info.total_inter_frame_delay; inbound_video->total_squared_inter_frame_delay = @@ -730,7 +801,7 @@ const std::string& ProduceIceCandidateStats(int64_t timestamp_us, candidate_stats->transport_id = transport_id; if (is_local) { candidate_stats->network_type = - NetworkAdapterTypeToStatsType(candidate.network_type()); + NetworkTypeToStatsType(candidate.network_type()); const std::string& candidate_type = candidate.type(); const std::string& relay_protocol = candidate.relay_protocol(); const std::string& url = candidate.url(); @@ -749,6 +820,16 @@ const std::string& ProduceIceCandidateStats(int64_t timestamp_us, candidate_stats->url = url; } } + if (candidate.network_type() == rtc::ADAPTER_TYPE_VPN) { + candidate_stats->vpn = true; + candidate_stats->network_adapter_type = + std::string(NetworkTypeToStatsNetworkAdapterType( + candidate.underlying_type_for_vpn())); + } else { + candidate_stats->vpn = false; + candidate_stats->network_adapter_type = std::string( + NetworkTypeToStatsNetworkAdapterType(candidate.network_type())); + } } else { // We don't expect to know the adapter type of remote candidates. RTC_DCHECK_EQ(rtc::ADAPTER_TYPE_UNKNOWN, candidate.network_type()); @@ -1850,7 +1931,8 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( inbound_audio->track_id = RTCMediaStreamTrackStatsIDFromDirectionAndAttachment( kReceiver, - track_media_info_map.GetAttachmentIdByTrack(audio_track).value()); + track_media_info_map.GetAttachmentIdByTrack(audio_track.get()) + .value()); } inbound_audio->transport_id = transport_id; // Remote-outbound. @@ -1881,7 +1963,8 @@ void RTCStatsCollector::ProduceAudioRTPStreamStats_n( track_media_info_map.GetAudioTrack(voice_sender_info); if (audio_track) { int attachment_id = - track_media_info_map.GetAttachmentIdByTrack(audio_track).value(); + track_media_info_map.GetAttachmentIdByTrack(audio_track.get()) + .value(); outbound_audio->track_id = RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(kSender, attachment_id); @@ -1942,7 +2025,8 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( inbound_video->track_id = RTCMediaStreamTrackStatsIDFromDirectionAndAttachment( kReceiver, - track_media_info_map.GetAttachmentIdByTrack(video_track).value()); + track_media_info_map.GetAttachmentIdByTrack(video_track.get()) + .value()); } inbound_video->transport_id = transport_id; report->AddStats(std::move(inbound_video)); @@ -1964,7 +2048,8 @@ void RTCStatsCollector::ProduceVideoRTPStreamStats_n( track_media_info_map.GetVideoTrack(video_sender_info); if (video_track) { int attachment_id = - track_media_info_map.GetAttachmentIdByTrack(video_track).value(); + track_media_info_map.GetAttachmentIdByTrack(video_track.get()) + .value(); outbound_video->track_id = RTCMediaStreamTrackStatsIDFromDirectionAndAttachment(kSender, attachment_id); @@ -2039,21 +2124,26 @@ void RTCStatsCollector::ProduceTransportStats_n( new RTCTransportStats(RTCTransportStatsIDFromTransportChannel( transport_name, channel_stats.component), timestamp_us)); - transport_stats->bytes_sent = 0; - transport_stats->packets_sent = 0; - transport_stats->bytes_received = 0; - transport_stats->packets_received = 0; + transport_stats->packets_sent = + channel_stats.ice_transport_stats.packets_sent; + transport_stats->packets_received = + channel_stats.ice_transport_stats.packets_received; + transport_stats->bytes_sent = + channel_stats.ice_transport_stats.bytes_sent; + transport_stats->bytes_received = + channel_stats.ice_transport_stats.bytes_received; transport_stats->dtls_state = DtlsTransportStateToRTCDtlsTransportState(channel_stats.dtls_state); transport_stats->selected_candidate_pair_changes = channel_stats.ice_transport_stats.selected_candidate_pair_changes; + transport_stats->ice_role = + IceRoleToRTCIceRole(channel_stats.ice_transport_stats.ice_role); + transport_stats->ice_local_username_fragment = + channel_stats.ice_transport_stats.ice_local_username_fragment; + transport_stats->ice_state = IceTransportStateToRTCIceTransportState( + channel_stats.ice_transport_stats.ice_state); for (const cricket::ConnectionInfo& info : channel_stats.ice_transport_stats.connection_infos) { - *transport_stats->bytes_sent += info.sent_total_bytes; - *transport_stats->packets_sent += - info.sent_total_packets - info.sent_discarded_packets; - *transport_stats->bytes_received += info.recv_total_bytes; - *transport_stats->packets_received += info.packets_received; if (info.best_connection) { transport_stats->selected_candidate_pair_id = RTCIceCandidatePairStatsIDFromConnectionInfo(info); @@ -2073,6 +2163,15 @@ void RTCStatsCollector::ProduceTransportStats_n( snprintf(bytes, sizeof(bytes), "%04X", channel_stats.ssl_version_bytes); transport_stats->tls_version = bytes; } + + if (channel_stats.dtls_role) { + transport_stats->dtls_role = *channel_stats.dtls_role == rtc::SSL_CLIENT + ? webrtc::RTCDtlsRole::kClient + : webrtc::RTCDtlsRole::kServer; + } else { + transport_stats->dtls_role = webrtc::RTCDtlsRole::kUnknown; + } + if (channel_stats.ssl_cipher_suite != rtc::kTlsNullWithNullNull && rtc::SSLStreamAdapter::SslCipherSuiteToName( channel_stats.ssl_cipher_suite) @@ -2128,11 +2227,9 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { transceiver_stats_infos_.clear(); // These are used to invoke GetStats for all the media channels together in // one worker thread hop. - std::map> + std::map> voice_stats; - std::map> + std::map> video_stats; auto transceivers = pc_->GetTransceiversInternal(); @@ -2164,16 +2261,14 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { stats.transport_name = std::string(channel->transport_name()); if (media_type == cricket::MEDIA_TYPE_AUDIO) { - auto* voice_channel = static_cast(channel); - RTC_DCHECK(voice_stats.find(voice_channel->media_channel()) == + RTC_DCHECK(voice_stats.find(channel->media_channel()) == voice_stats.end()); - voice_stats[voice_channel->media_channel()] = + voice_stats[channel->media_channel()] = std::make_unique(); } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - auto* video_channel = static_cast(channel); - RTC_DCHECK(video_stats.find(video_channel->media_channel()) == + RTC_DCHECK(video_stats.find(channel->media_channel()) == video_stats.end()); - video_stats[video_channel->media_channel()] = + video_stats[channel->media_channel()] = std::make_unique(); } else { RTC_DCHECK_NOTREACHED(); @@ -2189,13 +2284,15 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; for (const auto& entry : voice_stats) { - if (!entry.first->GetStats(entry.second.get(), - /*get_and_clear_legacy_stats=*/false)) { + if (!static_cast(entry.first) + ->GetStats(entry.second.get(), + /*get_and_clear_legacy_stats=*/false)) { RTC_LOG(LS_WARNING) << "Failed to get voice stats."; } } for (const auto& entry : video_stats) { - if (!entry.first->GetStats(entry.second.get())) { + if (!static_cast(entry.first) + ->GetStats(entry.second.get())) { RTC_LOG(LS_WARNING) << "Failed to get video stats."; } } @@ -2205,20 +2302,15 @@ void RTCStatsCollector::PrepareTransceiverStatsInfosAndCallStats_s_w_n() { auto transceiver = stats.transceiver; std::unique_ptr voice_media_info; std::unique_ptr video_media_info; - if (transceiver->channel()) { + auto channel = transceiver->channel(); + if (channel) { cricket::MediaType media_type = transceiver->media_type(); if (media_type == cricket::MEDIA_TYPE_AUDIO) { - auto* voice_channel = - static_cast(transceiver->channel()); - RTC_DCHECK(voice_stats[voice_channel->media_channel()]); - voice_media_info = - std::move(voice_stats[voice_channel->media_channel()]); + RTC_DCHECK(voice_stats[channel->media_channel()]); + voice_media_info = std::move(voice_stats[channel->media_channel()]); } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - auto* video_channel = - static_cast(transceiver->channel()); - RTC_DCHECK(video_stats[video_channel->media_channel()]); - video_media_info = - std::move(video_stats[video_channel->media_channel()]); + RTC_DCHECK(video_stats[channel->media_channel()]); + video_media_info = std::move(video_stats[channel->media_channel()]); } } std::vector> senders; diff --git a/pc/rtc_stats_collector.h b/pc/rtc_stats_collector.h index c84e6d3fef..e6d9d184fe 100644 --- a/pc/rtc_stats_collector.h +++ b/pc/rtc_stats_collector.h @@ -12,6 +12,8 @@ #define PC_RTC_STATS_COLLECTOR_H_ #include + +#include #include #include #include diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index 8f0936c26c..39999b6a09 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc @@ -11,29 +11,46 @@ #include "pc/rtc_stats_collector.h" #include +#include +#include #include #include #include #include #include +#include #include #include -#include "absl/memory/memory.h" #include "absl/strings/str_replace.h" +#include "api/candidate.h" #include "api/dtls_transport_interface.h" +#include "api/media_stream_interface.h" #include "api/media_stream_track.h" #include "api/rtp_parameters.h" +#include "api/stats/rtc_stats.h" #include "api/stats/rtc_stats_report.h" #include "api/stats/rtcstats_objects.h" #include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "api/video/recordable_encoded_frame.h" +#include "api/video/video_content_type.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" +#include "common_video/include/quality_limitation_reason.h" +#include "media/base/media_channel.h" +#include "modules/audio_processing/include/audio_processing_statistics.h" #include "modules/rtp_rtcp/include/report_block_data.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "p2p/base/connection_info.h" +#include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" #include "pc/media_stream.h" -#include "pc/test/fake_data_channel_provider.h" +#include "pc/stream_collection.h" +#include "pc/test/fake_data_channel_controller.h" #include "pc/test/fake_peer_connection_for_stats.h" #include "pc/test/mock_data_channel.h" #include "pc/test/mock_rtp_receiver_internal.h" @@ -43,10 +60,19 @@ #include "rtc_base/fake_clock.h" #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/gunit.h" -#include "rtc_base/logging.h" +#include "rtc_base/network_constants.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/rtc_certificate.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_fingerprint.h" +#include "rtc_base/ssl_identity.h" +#include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_encode.h" #include "rtc_base/strings/json.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/time_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" using ::testing::_; using ::testing::AtLeast; @@ -190,11 +216,14 @@ std::unique_ptr CreateFakeCandidate( const std::string& protocol, const rtc::AdapterType adapter_type, const std::string& candidate_type, - uint32_t priority) { + uint32_t priority, + const rtc::AdapterType underlying_type_for_vpn = + rtc::ADAPTER_TYPE_UNKNOWN) { std::unique_ptr candidate(new cricket::Candidate()); candidate->set_address(rtc::SocketAddress(hostname, port)); candidate->set_protocol(protocol); candidate->set_network_type(adapter_type); + candidate->set_underlying_type_for_vpn(underlying_type_for_vpn); candidate->set_type(candidate_type); candidate->set_priority(priority); return candidate; @@ -363,7 +392,6 @@ rtc::scoped_refptr CreateMockSender( EXPECT_CALL(*sender, AttachmentId()).WillRepeatedly(Return(attachment_id)); EXPECT_CALL(*sender, stream_ids()).WillRepeatedly(Return(local_stream_ids)); EXPECT_CALL(*sender, SetTransceiverAsStopped()); - EXPECT_CALL(*sender, Stop()); return sender; } @@ -389,7 +417,7 @@ rtc::scoped_refptr CreateMockReceiver( return params; })); EXPECT_CALL(*receiver, AttachmentId()).WillRepeatedly(Return(attachment_id)); - EXPECT_CALL(*receiver, StopAndEndTrack()); + EXPECT_CALL(*receiver, Stop()).WillRepeatedly(Return()); return receiver; } @@ -399,8 +427,8 @@ class RTCStatsCollectorWrapper { rtc::scoped_refptr pc) : pc_(pc), stats_collector_( - RTCStatsCollector::Create(pc, 50 * rtc::kNumMicrosecsPerMillisec)) { - } + RTCStatsCollector::Create(pc.get(), + 50 * rtc::kNumMicrosecsPerMillisec)) {} rtc::scoped_refptr stats_collector() { return stats_collector_; @@ -448,18 +476,22 @@ class RTCStatsCollectorWrapper { track = CreateFakeTrack(media_type, track_id, MediaStreamTrackInterface::kLive); if (add_stream) { - local_stream->AddTrack(static_cast(track.get())); + local_stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); } } else { track = CreateFakeTrack(media_type, track_id, MediaStreamTrackInterface::kLive); if (add_stream) { - local_stream->AddTrack(static_cast(track.get())); + local_stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); } } rtc::scoped_refptr sender = CreateMockSender(media_type, track, ssrc, attachment_id, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); return sender; } @@ -477,11 +509,13 @@ class RTCStatsCollectorWrapper { if (media_type == cricket::MEDIA_TYPE_AUDIO) { track = CreateFakeTrack(media_type, track_id, MediaStreamTrackInterface::kLive); - remote_stream->AddTrack(static_cast(track.get())); + remote_stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); } else { track = CreateFakeTrack(media_type, track_id, MediaStreamTrackInterface::kLive); - remote_stream->AddTrack(static_cast(track.get())); + remote_stream->AddTrack(rtc::scoped_refptr( + static_cast(track.get()))); } rtc::scoped_refptr receiver = @@ -490,6 +524,7 @@ class RTCStatsCollectorWrapper { .WillRepeatedly( Return(std::vector>( {remote_stream}))); + EXPECT_CALL(*receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(receiver); return receiver; } @@ -532,6 +567,7 @@ class RTCStatsCollectorWrapper { voice_sender_info.local_stats[0].ssrc, voice_sender_info.local_stats[0].ssrc + 10, local_stream_ids); EXPECT_CALL(*rtp_sender, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*rtp_sender, Stop()); pc_->AddSender(rtp_sender); } @@ -550,7 +586,7 @@ class RTCStatsCollectorWrapper { voice_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); - EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)); + EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(rtp_receiver); } @@ -569,6 +605,7 @@ class RTCStatsCollectorWrapper { video_sender_info.local_stats[0].ssrc, video_sender_info.local_stats[0].ssrc + 10, local_stream_ids); EXPECT_CALL(*rtp_sender, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*rtp_sender, Stop()); pc_->AddSender(rtp_sender); } @@ -587,7 +624,7 @@ class RTCStatsCollectorWrapper { video_receiver_info.local_stats[0].ssrc + 10); EXPECT_CALL(*rtp_receiver, streams()) .WillRepeatedly(Return(remote_streams)); - EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)); + EXPECT_CALL(*rtp_receiver, SetMediaChannel(_)).WillRepeatedly(Return()); pc_->AddReceiver(rtp_receiver); } @@ -598,7 +635,7 @@ class RTCStatsCollectorWrapper { private: rtc::scoped_refptr WaitForReport( rtc::scoped_refptr callback) { - EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(callback->report() != nullptr, kGetStatsReportTimeoutMs); int64_t after = rtc::TimeUTCMicros(); for (const RTCStats& stats : *callback->report()) { if (stats.type() == RTCRemoteInboundRtpStreamStats::kType || @@ -887,7 +924,7 @@ class RTCStatsCollectorTest : public ::testing::Test { TEST_F(RTCStatsCollectorTest, SingleCallback) { rtc::scoped_refptr result; stats_->stats_collector()->GetStatsReport(RTCStatsObtainer::Create(&result)); - EXPECT_TRUE_WAIT(result, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(result != nullptr, kGetStatsReportTimeoutMs); } TEST_F(RTCStatsCollectorTest, MultipleCallbacks) { @@ -895,9 +932,9 @@ TEST_F(RTCStatsCollectorTest, MultipleCallbacks) { stats_->stats_collector()->GetStatsReport(RTCStatsObtainer::Create(&a)); stats_->stats_collector()->GetStatsReport(RTCStatsObtainer::Create(&b)); stats_->stats_collector()->GetStatsReport(RTCStatsObtainer::Create(&c)); - EXPECT_TRUE_WAIT(a, kGetStatsReportTimeoutMs); - EXPECT_TRUE_WAIT(b, kGetStatsReportTimeoutMs); - EXPECT_TRUE_WAIT(c, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(a != nullptr, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(b != nullptr, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(c != nullptr, kGetStatsReportTimeoutMs); EXPECT_EQ(a.get(), b.get()); EXPECT_EQ(b.get(), c.get()); @@ -926,9 +963,9 @@ TEST_F(RTCStatsCollectorTest, MultipleCallbacksWithInvalidatedCacheInBetween) { // Cache is invalidated after 50 ms. fake_clock_.AdvanceTime(TimeDelta::Millis(51)); stats_->stats_collector()->GetStatsReport(RTCStatsObtainer::Create(&c)); - EXPECT_TRUE_WAIT(a, kGetStatsReportTimeoutMs); - EXPECT_TRUE_WAIT(b, kGetStatsReportTimeoutMs); - EXPECT_TRUE_WAIT(c, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(a != nullptr, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(b != nullptr, kGetStatsReportTimeoutMs); + EXPECT_TRUE_WAIT(c != nullptr, kGetStatsReportTimeoutMs); EXPECT_EQ(a.get(), b.get()); // The act of doing `AdvanceTime` processes all messages. If this was not the // case we might not require `c` to be fresher than `b`. @@ -1226,9 +1263,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) { TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { // Candidates in the first transport stats. - std::unique_ptr a_local_host = - CreateFakeCandidate("1.2.3.4", 5, "a_local_host's protocol", - rtc::ADAPTER_TYPE_VPN, cricket::LOCAL_PORT_TYPE, 0); + std::unique_ptr a_local_host = CreateFakeCandidate( + "1.2.3.4", 5, "a_local_host's protocol", rtc::ADAPTER_TYPE_VPN, + cricket::LOCAL_PORT_TYPE, 0, rtc::ADAPTER_TYPE_ETHERNET); RTCLocalIceCandidateStats expected_a_local_host( "RTCIceCandidate_" + a_local_host->id(), 0); expected_a_local_host.transport_id = "RTCTransport_a_0"; @@ -1239,6 +1276,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_host.protocol = "a_local_host's protocol"; expected_a_local_host.candidate_type = "host"; expected_a_local_host.priority = 0; + expected_a_local_host.vpn = true; + expected_a_local_host.network_adapter_type = RTCNetworkAdapterType::kEthernet; std::unique_ptr a_remote_srflx = CreateFakeCandidate( "6.7.8.9", 10, "remote_srflx's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1254,8 +1293,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_remote_srflx.priority = 1; std::unique_ptr a_local_prflx = CreateFakeCandidate( - "11.12.13.14", 15, "a_local_prflx's protocol", rtc::ADAPTER_TYPE_CELLULAR, - cricket::PRFLX_PORT_TYPE, 2); + "11.12.13.14", 15, "a_local_prflx's protocol", + rtc::ADAPTER_TYPE_CELLULAR_2G, cricket::PRFLX_PORT_TYPE, 2); RTCLocalIceCandidateStats expected_a_local_prflx( "RTCIceCandidate_" + a_local_prflx->id(), 0); expected_a_local_prflx.transport_id = "RTCTransport_a_0"; @@ -1266,6 +1305,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_prflx.protocol = "a_local_prflx's protocol"; expected_a_local_prflx.candidate_type = "prflx"; expected_a_local_prflx.priority = 2; + expected_a_local_prflx.vpn = false; + expected_a_local_prflx.network_adapter_type = + RTCNetworkAdapterType::kCellular2g; std::unique_ptr a_remote_relay = CreateFakeCandidate( "16.17.18.19", 20, "a_remote_relay's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1298,6 +1340,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_relay.candidate_type = "relay"; expected_a_local_relay.priority = 1; expected_a_local_relay.url = "turn:url1"; + expected_a_local_relay.vpn = false; + expected_a_local_relay.network_adapter_type = RTCNetworkAdapterType::kUnknown; std::unique_ptr a_local_relay_prflx = CreateFakeCandidate( "11.12.13.20", 22, "a_local_relay_prflx's protocol", @@ -1315,6 +1359,9 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_a_local_relay_prflx.relay_protocol = "udp"; expected_a_local_relay_prflx.candidate_type = "prflx"; expected_a_local_relay_prflx.priority = 1; + expected_a_local_relay_prflx.vpn = false; + expected_a_local_relay_prflx.network_adapter_type = + RTCNetworkAdapterType::kUnknown; // Candidates in the second transport stats. std::unique_ptr b_local = @@ -1330,6 +1377,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) { expected_b_local.protocol = "b_local's protocol"; expected_b_local.candidate_type = "host"; expected_b_local.priority = 42; + expected_b_local.vpn = false; + expected_b_local.network_adapter_type = RTCNetworkAdapterType::kWifi; std::unique_ptr b_remote = CreateFakeCandidate( "42.42.42.42", 42, "b_remote's protocol", rtc::ADAPTER_TYPE_UNKNOWN, @@ -1564,6 +1613,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) { expected_local_candidate.protocol = "protocol"; expected_local_candidate.candidate_type = "host"; expected_local_candidate.priority = 42; + expected_local_candidate.vpn = false; + expected_local_candidate.network_adapter_type = RTCNetworkAdapterType::kWifi; ASSERT_TRUE(report->Get(expected_local_candidate.id())); EXPECT_EQ(expected_local_candidate, report->Get(expected_local_candidate.id()) @@ -1598,13 +1649,13 @@ TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) { } // TODO(bugs.webrtc.org/11547): Supply a separate network thread. - FakeDataChannelProvider provider; + FakeDataChannelController controller; rtc::scoped_refptr dummy_channel_a = SctpDataChannel::Create( - &provider, "DummyChannelA", InternalDataChannelInit(), + &controller, "DummyChannelA", InternalDataChannelInit(), rtc::Thread::Current(), rtc::Thread::Current()); pc_->SignalSctpDataChannelCreated()(dummy_channel_a.get()); rtc::scoped_refptr dummy_channel_b = SctpDataChannel::Create( - &provider, "DummyChannelB", InternalDataChannelInit(), + &controller, "DummyChannelB", InternalDataChannelInit(), rtc::Thread::Current(), rtc::Thread::Current()); pc_->SignalSctpDataChannelCreated()(dummy_channel_b.get()); @@ -1685,8 +1736,8 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr local_audio_track = CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", MediaStreamTrackInterface::kEnded); - local_stream->AddTrack( - static_cast(local_audio_track.get())); + local_stream->AddTrack(rtc::scoped_refptr( + static_cast(local_audio_track.get()))); cricket::VoiceSenderInfo voice_sender_info_ssrc1; voice_sender_info_ssrc1.local_stats.push_back(cricket::SsrcSenderInfo()); @@ -1701,10 +1752,10 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr report = stats_->GetStatsReport(); RTCMediaStreamStats expected_local_stream( - IdForType(report), report->timestamp_us()); + IdForType(report.get()), report->timestamp_us()); expected_local_stream.stream_identifier = local_stream->id(); expected_local_stream.track_ids = { - IdForType(report)}; + IdForType(report.get())}; ASSERT_TRUE(report->Get(expected_local_stream.id())) << "Did not find " << expected_local_stream.id() << " in " << report->ToJson(); @@ -1713,7 +1764,7 @@ TEST_F(RTCStatsCollectorTest, report->Get(expected_local_stream.id())->cast_to()); RTCMediaStreamTrackStats expected_local_audio_track_ssrc1( - IdForType(report), report->timestamp_us(), + IdForType(report.get()), report->timestamp_us(), RTCMediaStreamTrackKind::kAudio); expected_local_audio_track_ssrc1.track_identifier = local_audio_track->id(); expected_local_audio_track_ssrc1.media_source_id = @@ -1741,8 +1792,8 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr remote_audio_track = CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "RemoteAudioTrackID", MediaStreamTrackInterface::kLive); - remote_stream->AddTrack( - static_cast(remote_audio_track.get())); + remote_stream->AddTrack(rtc::scoped_refptr( + static_cast(remote_audio_track.get()))); cricket::VoiceReceiverInfo voice_receiver_info; voice_receiver_info.local_stats.push_back(cricket::SsrcReceiverInfo()); @@ -1772,10 +1823,10 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr report = stats_->GetStatsReport(); RTCMediaStreamStats expected_remote_stream( - IdForType(report), report->timestamp_us()); + IdForType(report.get()), report->timestamp_us()); expected_remote_stream.stream_identifier = remote_stream->id(); - expected_remote_stream.track_ids = - std::vector({IdForType(report)}); + expected_remote_stream.track_ids = std::vector( + {IdForType(report.get())}); ASSERT_TRUE(report->Get(expected_remote_stream.id())) << "Did not find " << expected_remote_stream.id() << " in " << report->ToJson(); @@ -1784,7 +1835,7 @@ TEST_F(RTCStatsCollectorTest, report->Get(expected_remote_stream.id())->cast_to()); RTCMediaStreamTrackStats expected_remote_audio_track( - IdForType(report), report->timestamp_us(), + IdForType(report.get()), report->timestamp_us(), RTCMediaStreamTrackKind::kAudio); expected_remote_audio_track.track_identifier = remote_audio_track->id(); // `expected_remote_audio_track.media_source_id` should be undefined @@ -1825,8 +1876,8 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr local_video_track = CreateFakeTrack(cricket::MEDIA_TYPE_VIDEO, "LocalVideoTrackID", MediaStreamTrackInterface::kLive); - local_stream->AddTrack( - static_cast(local_video_track.get())); + local_stream->AddTrack(rtc::scoped_refptr( + static_cast(local_video_track.get()))); cricket::VideoSenderInfo video_sender_info_ssrc1; video_sender_info_ssrc1.local_stats.push_back(cricket::SsrcSenderInfo()); @@ -1888,8 +1939,8 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr remote_video_track_ssrc3 = CreateFakeTrack(cricket::MEDIA_TYPE_VIDEO, "RemoteVideoTrackID3", MediaStreamTrackInterface::kEnded); - remote_stream->AddTrack( - static_cast(remote_video_track_ssrc3.get())); + remote_stream->AddTrack(rtc::scoped_refptr( + static_cast(remote_video_track_ssrc3.get()))); cricket::VideoReceiverInfo video_receiver_info_ssrc3; video_receiver_info_ssrc3.local_stats.push_back(cricket::SsrcReceiverInfo()); @@ -2088,6 +2139,8 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { video_media_info.receivers[0].frames_dropped = 13; video_media_info.receivers[0].qp_sum = absl::nullopt; video_media_info.receivers[0].total_decode_time_ms = 9000; + video_media_info.receivers[0].total_processing_delay = + webrtc::TimeDelta::Millis(600); video_media_info.receivers[0].total_inter_frame_delay = 0.123; video_media_info.receivers[0].total_squared_inter_frame_delay = 0.00456; video_media_info.receivers[0].jitter_ms = 1199; @@ -2121,7 +2174,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.ssrc = 1; expected_video.media_type = "video"; expected_video.kind = "video"; - expected_video.track_id = IdForType(report); + expected_video.track_id = IdForType(report.get()); expected_video.transport_id = "RTCTransport_TransportName_1"; expected_video.codec_id = "RTCCodec_VideoMid_Inbound_42"; expected_video.fir_count = 5; @@ -2137,6 +2190,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCInboundRTPStreamStats_Video) { expected_video.frames_dropped = 13; // `expected_video.qp_sum` should be undefined. expected_video.total_decode_time = 9.0; + expected_video.total_processing_delay = 0.6; expected_video.total_inter_frame_delay = 0.123; expected_video.total_squared_inter_frame_delay = 0.00456; expected_video.jitter = 1.199; @@ -2212,7 +2266,7 @@ TEST_F(RTCStatsCollectorTest, CollectRTCOutboundRTPStreamStats_Audio) { expected_audio.ssrc = 1; expected_audio.media_type = "audio"; expected_audio.kind = "audio"; - expected_audio.track_id = IdForType(report); + expected_audio.track_id = IdForType(report.get()); expected_audio.transport_id = "RTCTransport_TransportName_1"; expected_audio.codec_id = "RTCCodec_AudioMid_Outbound_42"; expected_audio.packets_sent = 2; @@ -2389,8 +2443,14 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( rtp_connection_info); rtp_transport_channel_stats.dtls_state = DtlsTransportState::kNew; + rtp_transport_channel_stats.ice_transport_stats.bytes_sent = 42; + rtp_transport_channel_stats.ice_transport_stats.packets_sent = 1; + rtp_transport_channel_stats.ice_transport_stats.bytes_received = 1337; + rtp_transport_channel_stats.ice_transport_stats.packets_received = 4; rtp_transport_channel_stats.ice_transport_stats .selected_candidate_pair_changes = 1; + rtp_transport_channel_stats.ice_transport_stats.ice_local_username_fragment = + "thelocalufrag"; pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats}); // Get stats without RTCP, an active connection or certificates. @@ -2405,7 +2465,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { expected_rtp_transport.bytes_received = 1337; expected_rtp_transport.packets_received = 4; expected_rtp_transport.dtls_state = RTCDtlsTransportState::kNew; + expected_rtp_transport.dtls_role = RTCDtlsRole::kUnknown; expected_rtp_transport.selected_candidate_pair_changes = 1; + expected_rtp_transport.ice_role = RTCIceRole::kUnknown; + expected_rtp_transport.ice_local_username_fragment = "thelocalufrag"; + expected_rtp_transport.ice_state = RTCIceTransportState::kNew; ASSERT_TRUE(report->Get(expected_rtp_transport.id())); EXPECT_EQ( @@ -2427,6 +2491,14 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { rtcp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( rtcp_connection_info); rtcp_transport_channel_stats.dtls_state = DtlsTransportState::kConnecting; + rtcp_transport_channel_stats.ice_transport_stats.bytes_sent = 1337; + rtcp_transport_channel_stats.ice_transport_stats.packets_sent = 1; + rtcp_transport_channel_stats.ice_transport_stats.bytes_received = 42; + rtcp_transport_channel_stats.ice_transport_stats.packets_received = 4; + rtcp_transport_channel_stats.ice_transport_stats.ice_local_username_fragment = + "thelocalufrag"; + rtcp_transport_channel_stats.ice_transport_stats.ice_state = + IceTransportState::kChecking; pc_->SetTransportStats(kTransportName, {rtp_transport_channel_stats, rtcp_transport_channel_stats}); @@ -2442,7 +2514,11 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStats) { expected_rtcp_transport.bytes_received = 42; expected_rtcp_transport.packets_received = 4; expected_rtcp_transport.dtls_state = RTCDtlsTransportState::kConnecting; + expected_rtcp_transport.dtls_role = RTCDtlsRole::kUnknown; expected_rtcp_transport.selected_candidate_pair_changes = 0; + expected_rtcp_transport.ice_role = RTCIceRole::kUnknown; + expected_rtcp_transport.ice_local_username_fragment = "thelocalufrag"; + expected_rtcp_transport.ice_state = RTCIceTransportState::kChecking; expected_rtp_transport.rtcp_transport_stats_id = expected_rtcp_transport.id(); ASSERT_TRUE(report->Get(expected_rtp_transport.id())); @@ -2532,11 +2608,6 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { rtp_connection_info.best_connection = false; rtp_connection_info.local_candidate = *rtp_local_candidate.get(); rtp_connection_info.remote_candidate = *rtp_remote_candidate.get(); - rtp_connection_info.sent_total_bytes = 42; - rtp_connection_info.recv_total_bytes = 1337; - rtp_connection_info.sent_total_packets = 3; - rtp_connection_info.sent_discarded_packets = 2; - rtp_connection_info.packets_received = 4; cricket::TransportChannelStats rtp_transport_channel_stats; rtp_transport_channel_stats.component = cricket::ICE_CANDIDATE_COMPONENT_RTP; rtp_transport_channel_stats.ice_transport_stats.connection_infos.push_back( @@ -2546,6 +2617,13 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { rtp_transport_channel_stats.ice_transport_stats .selected_candidate_pair_changes = 1; rtp_transport_channel_stats.ssl_version_bytes = 0x0203; + rtp_transport_channel_stats.dtls_role = rtc::SSL_CLIENT; + rtp_transport_channel_stats.ice_transport_stats.ice_role = + cricket::ICEROLE_CONTROLLING; + rtp_transport_channel_stats.ice_transport_stats.ice_local_username_fragment = + "thelocalufrag"; + rtp_transport_channel_stats.ice_transport_stats.ice_state = + IceTransportState::kConnected; // 0x2F is TLS_RSA_WITH_AES_128_CBC_SHA according to IANA rtp_transport_channel_stats.ssl_cipher_suite = 0x2F; rtp_transport_channel_stats.srtp_crypto_suite = rtc::kSrtpAes128CmSha1_80; @@ -2558,14 +2636,19 @@ TEST_F(RTCStatsCollectorTest, CollectRTCTransportStatsWithCrypto) { "RTCTransport_transport_" + rtc::ToString(cricket::ICE_CANDIDATE_COMPONENT_RTP), report->timestamp_us()); - expected_rtp_transport.bytes_sent = 42; - expected_rtp_transport.packets_sent = 1; - expected_rtp_transport.bytes_received = 1337; - expected_rtp_transport.packets_received = 4; expected_rtp_transport.dtls_state = RTCDtlsTransportState::kConnected; expected_rtp_transport.selected_candidate_pair_changes = 1; + expected_rtp_transport.ice_role = RTCIceRole::kUnknown; + expected_rtp_transport.bytes_sent = 0; + expected_rtp_transport.bytes_received = 0; + expected_rtp_transport.packets_sent = 0; + expected_rtp_transport.packets_received = 0; + expected_rtp_transport.ice_role = RTCIceRole::kControlling; + expected_rtp_transport.ice_local_username_fragment = "thelocalufrag"; + expected_rtp_transport.ice_state = "connected"; // Crypto parameters expected_rtp_transport.tls_version = "0203"; + expected_rtp_transport.dtls_role = RTCDtlsRole::kClient; expected_rtp_transport.dtls_cipher = "TLS_RSA_WITH_AES_128_CBC_SHA"; expected_rtp_transport.srtp_cipher = "AES_CM_128_HMAC_SHA1_80"; @@ -2611,7 +2694,7 @@ TEST_F(RTCStatsCollectorTest, CollectNoStreamRTCOutboundRTPStreamStats_Audio) { expected_audio.ssrc = 1; expected_audio.media_type = "audio"; expected_audio.kind = "audio"; - expected_audio.track_id = IdForType(report); + expected_audio.track_id = IdForType(report.get()); expected_audio.transport_id = "RTCTransport_TransportName_1"; expected_audio.codec_id = "RTCCodec_AudioMid_Outbound_42"; expected_audio.packets_sent = 2; @@ -2691,6 +2774,8 @@ TEST_F(RTCStatsCollectorTest, RTCVideoSourceStatsCollectedForSenderWithTrack) { "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2734,6 +2819,8 @@ TEST_F(RTCStatsCollectorTest, "LocalVideoTrackID", MediaStreamTrackInterface::kLive, video_source); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kNoSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2763,6 +2850,8 @@ TEST_F(RTCStatsCollectorTest, /*source=*/nullptr); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, video_track, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -2785,6 +2874,8 @@ TEST_F(RTCStatsCollectorTest, pc_->AddVoiceChannel("AudioMid", "TransportName", voice_media_info); rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_AUDIO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -3108,6 +3199,8 @@ TEST_F(RTCStatsCollectorTest, rtc::scoped_refptr sender = CreateMockSender( cricket::MEDIA_TYPE_VIDEO, /*track=*/nullptr, kSsrc, kAttachmentId, {}); + EXPECT_CALL(*sender, Stop()); + EXPECT_CALL(*sender, SetMediaChannel(_)); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -3126,8 +3219,8 @@ TEST_F(RTCStatsCollectorTest, CollectEchoReturnLossFromTrackAudioProcessor) { CreateFakeTrack(cricket::MEDIA_TYPE_AUDIO, "LocalAudioTrackID", MediaStreamTrackInterface::kEnded, /*create_fake_audio_processor=*/true); - local_stream->AddTrack( - static_cast(local_audio_track.get())); + local_stream->AddTrack(rtc::scoped_refptr( + static_cast(local_audio_track.get()))); cricket::VoiceSenderInfo voice_sender_info_ssrc1; voice_sender_info_ssrc1.local_stats.push_back(cricket::SsrcSenderInfo()); @@ -3140,7 +3233,7 @@ TEST_F(RTCStatsCollectorTest, CollectEchoReturnLossFromTrackAudioProcessor) { rtc::scoped_refptr report = stats_->GetStatsReport(); RTCMediaStreamTrackStats expected_local_audio_track_ssrc1( - IdForType(report), report->timestamp_us(), + IdForType(report.get()), report->timestamp_us(), RTCMediaStreamTrackKind::kAudio); expected_local_audio_track_ssrc1.track_identifier = local_audio_track->id(); expected_local_audio_track_ssrc1.media_source_id = @@ -3257,6 +3350,7 @@ TEST_F(RTCStatsCollectorTest, StatsReportedOnZeroSsrc) { MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 0, 49, {}); + EXPECT_CALL(*sender, Stop()); pc_->AddSender(sender); rtc::scoped_refptr report = stats_->GetStatsReport(); @@ -3276,6 +3370,7 @@ TEST_F(RTCStatsCollectorTest, DoNotCrashOnSsrcChange) { MediaStreamTrackInterface::kLive); rtc::scoped_refptr sender = CreateMockSender(cricket::MEDIA_TYPE_AUDIO, track, 4711, 49, {}); + EXPECT_CALL(*sender, Stop()); pc_->AddSender(sender); // We do not generate any matching voice_sender_info stats. @@ -3431,7 +3526,8 @@ class FakeRTCStatsCollector : public RTCStatsCollector, TEST(RTCStatsCollectorTestWithFakeCollector, ThreadUsageAndResultsMerging) { auto pc = rtc::make_ref_counted(); rtc::scoped_refptr stats_collector( - FakeRTCStatsCollector::Create(pc, 50 * rtc::kNumMicrosecsPerMillisec)); + FakeRTCStatsCollector::Create(pc.get(), + 50 * rtc::kNumMicrosecsPerMillisec)); stats_collector->VerifyThreadUsageAndResultsMerging(); } diff --git a/pc/rtc_stats_integrationtest.cc b/pc/rtc_stats_integrationtest.cc index ad533499ab..4f64334ffb 100644 --- a/pc/rtc_stats_integrationtest.cc +++ b/pc/rtc_stats_integrationtest.cc @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -19,8 +18,6 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/audio_options.h" @@ -177,8 +174,8 @@ class RTCStatsIntegrationTest : public ::testing::Test { PeerConnectionInterface* pc) { rtc::scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); - pc->GetStats(stats_obtainer); - EXPECT_TRUE_WAIT(stats_obtainer->report(), kGetStatsTimeoutMs); + pc->GetStats(stats_obtainer.get()); + EXPECT_TRUE_WAIT(stats_obtainer->report() != nullptr, kGetStatsTimeoutMs); return stats_obtainer->report(); } @@ -189,7 +186,7 @@ class RTCStatsIntegrationTest : public ::testing::Test { rtc::scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); pc->GetStats(selector, stats_obtainer); - EXPECT_TRUE_WAIT(stats_obtainer->report(), kGetStatsTimeoutMs); + EXPECT_TRUE_WAIT(stats_obtainer->report() != nullptr, kGetStatsTimeoutMs); return stats_obtainer->report(); } @@ -435,7 +432,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCCertificateStats(const RTCCertificateStats& certificate) { - RTCStatsVerifier verifier(report_, &certificate); + RTCStatsVerifier verifier(report_.get(), &certificate); verifier.TestMemberIsDefined(certificate.fingerprint); verifier.TestMemberIsDefined(certificate.fingerprint_algorithm); verifier.TestMemberIsDefined(certificate.base64_certificate); @@ -445,7 +442,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCCodecStats(const RTCCodecStats& codec) { - RTCStatsVerifier verifier(report_, &codec); + RTCStatsVerifier verifier(report_.get(), &codec); verifier.TestMemberIsIDReference(codec.transport_id, RTCTransportStats::kType); verifier.TestMemberIsDefined(codec.payload_type); @@ -463,7 +460,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCDataChannelStats(const RTCDataChannelStats& data_channel) { - RTCStatsVerifier verifier(report_, &data_channel); + RTCStatsVerifier verifier(report_.get(), &data_channel); verifier.TestMemberIsDefined(data_channel.label); verifier.TestMemberIsDefined(data_channel.protocol); verifier.TestMemberIsDefined(data_channel.data_channel_identifier); @@ -478,7 +475,7 @@ class RTCStatsReportVerifier { bool VerifyRTCIceCandidatePairStats( const RTCIceCandidatePairStats& candidate_pair, bool is_selected_pair) { - RTCStatsVerifier verifier(report_, &candidate_pair); + RTCStatsVerifier verifier(report_.get(), &candidate_pair); verifier.TestMemberIsIDReference(candidate_pair.transport_id, RTCTransportStats::kType); verifier.TestMemberIsIDReference(candidate_pair.local_candidate_id, @@ -524,18 +521,23 @@ class RTCStatsReportVerifier { candidate_pair.consent_requests_sent); verifier.TestMemberIsUndefined(candidate_pair.consent_responses_received); verifier.TestMemberIsUndefined(candidate_pair.consent_responses_sent); + return verifier.ExpectAllMembersSuccessfullyTested(); } bool VerifyRTCIceCandidateStats(const RTCIceCandidateStats& candidate) { - RTCStatsVerifier verifier(report_, &candidate); + RTCStatsVerifier verifier(report_.get(), &candidate); verifier.TestMemberIsIDReference(candidate.transport_id, RTCTransportStats::kType); verifier.TestMemberIsDefined(candidate.is_remote); if (*candidate.is_remote) { verifier.TestMemberIsUndefined(candidate.network_type); + verifier.TestMemberIsUndefined(candidate.network_adapter_type); + verifier.TestMemberIsUndefined(candidate.vpn); } else { verifier.TestMemberIsDefined(candidate.network_type); + verifier.TestMemberIsDefined(candidate.network_adapter_type); + verifier.TestMemberIsDefined(candidate.vpn); } verifier.TestMemberIsDefined(candidate.ip); verifier.TestMemberIsDefined(candidate.address); @@ -559,7 +561,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCMediaStreamStats(const RTCMediaStreamStats& media_stream) { - RTCStatsVerifier verifier(report_, &media_stream); + RTCStatsVerifier verifier(report_.get(), &media_stream); verifier.TestMemberIsDefined(media_stream.stream_identifier); verifier.TestMemberIsIDReference(media_stream.track_ids, RTCMediaStreamTrackStats::kType); @@ -568,7 +570,7 @@ class RTCStatsReportVerifier { bool VerifyRTCMediaStreamTrackStats( const RTCMediaStreamTrackStats& media_stream_track) { - RTCStatsVerifier verifier(report_, &media_stream_track); + RTCStatsVerifier verifier(report_.get(), &media_stream_track); verifier.TestMemberIsDefined(media_stream_track.track_identifier); verifier.TestMemberIsDefined(media_stream_track.remote_source); verifier.TestMemberIsDefined(media_stream_track.ended); @@ -769,7 +771,7 @@ class RTCStatsReportVerifier { bool VerifyRTCPeerConnectionStats( const RTCPeerConnectionStats& peer_connection) { - RTCStatsVerifier verifier(report_, &peer_connection); + RTCStatsVerifier verifier(report_.get(), &peer_connection); verifier.TestMemberIsNonNegative( peer_connection.data_channels_opened); verifier.TestMemberIsNonNegative( @@ -806,13 +808,13 @@ class RTCStatsReportVerifier { bool VerifyRTCInboundRTPStreamStats( const RTCInboundRTPStreamStats& inbound_stream) { - RTCStatsVerifier verifier(report_, &inbound_stream); + RTCStatsVerifier verifier(report_.get(), &inbound_stream); VerifyRTCReceivedRtpStreamStats(inbound_stream, verifier, - inbound_stream.media_type.is_defined() && + inbound_stream.kind.is_defined() && *inbound_stream.media_type == "audio"); verifier.TestMemberIsOptionalIDReference( inbound_stream.remote_id, RTCRemoteOutboundRtpStreamStats::kType); - if (inbound_stream.media_type.is_defined() && + if (inbound_stream.kind.is_defined() && *inbound_stream.media_type == "video") { verifier.TestMemberIsNonNegative(inbound_stream.qp_sum); verifier.TestMemberIsDefined(inbound_stream.decoder_implementation); @@ -821,7 +823,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(inbound_stream.decoder_implementation); } verifier.TestMemberIsNonNegative(inbound_stream.packets_received); - if (inbound_stream.media_type.is_defined() && + if (inbound_stream.kind.is_defined() && *inbound_stream.media_type == "audio") { verifier.TestMemberIsNonNegative( inbound_stream.fec_packets_received); @@ -853,7 +855,7 @@ class RTCStatsReportVerifier { inbound_stream.jitter_buffer_delay); verifier.TestMemberIsNonNegative( inbound_stream.jitter_buffer_emitted_count); - if (inbound_stream.media_type.is_defined() && + if (inbound_stream.kind.is_defined() && *inbound_stream.media_type == "video") { verifier.TestMemberIsUndefined(inbound_stream.total_samples_received); verifier.TestMemberIsUndefined(inbound_stream.concealed_samples); @@ -905,13 +907,15 @@ class RTCStatsReportVerifier { // Test runtime too short to get an estimate (at least two RTCP sender // reports need to be received). verifier.MarkMemberTested(inbound_stream.estimated_playout_timestamp, true); - if (inbound_stream.media_type.is_defined() && + if (inbound_stream.kind.is_defined() && *inbound_stream.media_type == "video") { verifier.TestMemberIsDefined(inbound_stream.frames_decoded); verifier.TestMemberIsDefined(inbound_stream.key_frames_decoded); verifier.TestMemberIsNonNegative(inbound_stream.frames_dropped); verifier.TestMemberIsNonNegative( inbound_stream.total_decode_time); + verifier.TestMemberIsNonNegative( + inbound_stream.total_processing_delay); verifier.TestMemberIsNonNegative( inbound_stream.total_inter_frame_delay); verifier.TestMemberIsNonNegative( @@ -924,6 +928,7 @@ class RTCStatsReportVerifier { verifier.TestMemberIsUndefined(inbound_stream.key_frames_decoded); verifier.TestMemberIsUndefined(inbound_stream.frames_dropped); verifier.TestMemberIsUndefined(inbound_stream.total_decode_time); + verifier.TestMemberIsUndefined(inbound_stream.total_processing_delay); verifier.TestMemberIsUndefined(inbound_stream.total_inter_frame_delay); verifier.TestMemberIsUndefined( inbound_stream.total_squared_inter_frame_delay); @@ -934,9 +939,9 @@ class RTCStatsReportVerifier { bool VerifyRTCOutboundRTPStreamStats( const RTCOutboundRTPStreamStats& outbound_stream) { - RTCStatsVerifier verifier(report_, &outbound_stream); + RTCStatsVerifier verifier(report_.get(), &outbound_stream); VerifyRTCRTPStreamStats(outbound_stream, verifier); - if (outbound_stream.media_type.is_defined() && + if (outbound_stream.kind.is_defined() && *outbound_stream.media_type == "video") { verifier.TestMemberIsIDReference(outbound_stream.media_source_id, RTCVideoSourceStats::kType); @@ -965,7 +970,7 @@ class RTCStatsReportVerifier { outbound_stream.header_bytes_sent); verifier.TestMemberIsNonNegative( outbound_stream.retransmitted_bytes_sent); - if (outbound_stream.media_type.is_defined() && + if (outbound_stream.kind.is_defined() && *outbound_stream.media_type == "video") { verifier.TestMemberIsDefined(outbound_stream.frames_encoded); verifier.TestMemberIsDefined(outbound_stream.key_frames_encoded); @@ -1046,7 +1051,7 @@ class RTCStatsReportVerifier { bool VerifyRTCRemoteInboundRtpStreamStats( const RTCRemoteInboundRtpStreamStats& remote_inbound_stream) { - RTCStatsVerifier verifier(report_, &remote_inbound_stream); + RTCStatsVerifier verifier(report_.get(), &remote_inbound_stream); VerifyRTCReceivedRtpStreamStats(remote_inbound_stream, verifier, false); verifier.TestMemberIsDefined(remote_inbound_stream.fraction_lost); verifier.TestMemberIsIDReference(remote_inbound_stream.local_id, @@ -1062,7 +1067,7 @@ class RTCStatsReportVerifier { bool VerifyRTCRemoteOutboundRTPStreamStats( const RTCRemoteOutboundRtpStreamStats& remote_outbound_stream) { - RTCStatsVerifier verifier(report_, &remote_outbound_stream); + RTCStatsVerifier verifier(report_.get(), &remote_outbound_stream); VerifyRTCRTPStreamStats(remote_outbound_stream, verifier); VerifyRTCSentRTPStreamStats(remote_outbound_stream, verifier); verifier.TestMemberIsIDReference(remote_outbound_stream.local_id, @@ -1086,7 +1091,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCAudioSourceStats(const RTCAudioSourceStats& audio_source) { - RTCStatsVerifier verifier(report_, &audio_source); + RTCStatsVerifier verifier(report_.get(), &audio_source); VerifyRTCMediaSourceStats(audio_source, &verifier); // Audio level, unlike audio energy, only gets updated at a certain // frequency, so we don't require that one to be positive to avoid a race @@ -1104,7 +1109,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCVideoSourceStats(const RTCVideoSourceStats& video_source) { - RTCStatsVerifier verifier(report_, &video_source); + RTCStatsVerifier verifier(report_.get(), &video_source); VerifyRTCMediaSourceStats(video_source, &verifier); // TODO(hbos): This integration test uses fakes that doesn't support // VideoTrackSourceInterface::Stats. When this is fixed we should @@ -1118,7 +1123,7 @@ class RTCStatsReportVerifier { } bool VerifyRTCTransportStats(const RTCTransportStats& transport) { - RTCStatsVerifier verifier(report_, &transport); + RTCStatsVerifier verifier(report_.get(), &transport); verifier.TestMemberIsNonNegative(transport.bytes_sent); verifier.TestMemberIsNonNegative(transport.packets_sent); verifier.TestMemberIsNonNegative(transport.bytes_received); @@ -1134,9 +1139,13 @@ class RTCStatsReportVerifier { RTCCertificateStats::kType); verifier.TestMemberIsDefined(transport.tls_version); verifier.TestMemberIsDefined(transport.dtls_cipher); + verifier.TestMemberIsDefined(transport.dtls_role); verifier.TestMemberIsDefined(transport.srtp_cipher); verifier.TestMemberIsPositive( transport.selected_candidate_pair_changes); + verifier.TestMemberIsDefined(transport.ice_role); + verifier.TestMemberIsDefined(transport.ice_local_username_fragment); + verifier.TestMemberIsDefined(transport.ice_state); return verifier.ExpectAllMembersSuccessfullyTested(); } @@ -1238,7 +1247,7 @@ TEST_F(RTCStatsIntegrationTest, rtc::scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); - caller_->pc()->GetStats(stats_obtainer); + caller_->pc()->GetStats(stats_obtainer.get()); // This will destroy the peer connection. caller_ = nullptr; // Any pending stats requests should have completed in the act of destroying @@ -1255,7 +1264,7 @@ TEST_F(RTCStatsIntegrationTest, GetsStatsWhileClosingPeerConnection) { rtc::scoped_refptr stats_obtainer = RTCStatsObtainer::Create(); - caller_->pc()->GetStats(stats_obtainer); + caller_->pc()->GetStats(stats_obtainer.get()); caller_->pc()->Close(); ASSERT_TRUE(stats_obtainer->report()); diff --git a/pc/rtc_stats_traversal_unittest.cc b/pc/rtc_stats_traversal_unittest.cc index c7d097117e..6e9b784313 100644 --- a/pc/rtc_stats_traversal_unittest.cc +++ b/pc/rtc_stats_traversal_unittest.cc @@ -11,7 +11,6 @@ #include "pc/rtc_stats_traversal.h" #include -#include #include #include "api/stats/rtcstats_objects.h" diff --git a/pc/rtp_media_utils.h b/pc/rtp_media_utils.h index 6f7986f096..240274fe05 100644 --- a/pc/rtp_media_utils.h +++ b/pc/rtp_media_utils.h @@ -11,8 +11,9 @@ #ifndef PC_RTP_MEDIA_UTILS_H_ #define PC_RTP_MEDIA_UTILS_H_ +#include // no-presubmit-check TODO(webrtc:8982) + #include "api/rtp_transceiver_direction.h" -#include "api/rtp_transceiver_interface.h" namespace webrtc { diff --git a/pc/rtp_parameters_conversion.cc b/pc/rtp_parameters_conversion.cc index 8d3064ed93..afba4bc94f 100644 --- a/pc/rtp_parameters_conversion.cc +++ b/pc/rtp_parameters_conversion.cc @@ -10,10 +10,10 @@ #include "pc/rtp_parameters_conversion.h" -#include #include #include #include +#include #include #include "api/array_view.h" diff --git a/pc/rtp_parameters_conversion.h b/pc/rtp_parameters_conversion.h index 62e4685722..959f3fde47 100644 --- a/pc/rtp_parameters_conversion.h +++ b/pc/rtp_parameters_conversion.h @@ -11,7 +11,6 @@ #ifndef PC_RTP_PARAMETERS_CONVERSION_H_ #define PC_RTP_PARAMETERS_CONVERSION_H_ -#include #include #include "absl/types/optional.h" diff --git a/pc/rtp_parameters_conversion_unittest.cc b/pc/rtp_parameters_conversion_unittest.cc index 99d976abcd..50d90e1c30 100644 --- a/pc/rtp_parameters_conversion_unittest.cc +++ b/pc/rtp_parameters_conversion_unittest.cc @@ -10,10 +10,13 @@ #include "pc/rtp_parameters_conversion.h" -#include +#include +#include +#include -#include "rtc_base/gunit.h" +#include "api/media_types.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::UnorderedElementsAre; diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc index 2444c9b60d..a2b3353c0e 100644 --- a/pc/rtp_receiver.cc +++ b/pc/rtp_receiver.cc @@ -17,7 +17,7 @@ #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" -#include "rtc_base/location.h" +#include "rtc_base/thread.h" namespace webrtc { diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 73fc5b9858..d591c464af 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h @@ -42,16 +42,18 @@ namespace webrtc { // Internal class used by PeerConnection. class RtpReceiverInternal : public RtpReceiverInterface { public: - // Stops receiving. The track may be reactivated. + // Call on the signaling thread, to let the receiver know that the the + // embedded source object should enter a stopped/ended state and the track's + // state set to `kEnded`, a final state that cannot be reversed. virtual void Stop() = 0; - // Stops the receiver permanently. - // Causes the associated track to enter kEnded state. Cannot be reversed. - virtual void StopAndEndTrack() = 0; // Sets the underlying MediaEngine channel associated with this RtpSender. // A VoiceMediaChannel should be used for audio RtpSenders and // a VideoMediaChannel should be used for video RtpSenders. - // Must call SetMediaChannel(nullptr) before the media channel is destroyed. + // NOTE: + // * SetMediaChannel(nullptr) must be called before the media channel is + // destroyed. + // * This method must be invoked on the worker thread. virtual void SetMediaChannel(cricket::MediaChannel* media_channel) = 0; // Configures the RtpReceiver with the underlying media channel, with the diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 110b5aae0a..b8cca6c21b 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -299,8 +300,8 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) { // we need to copy. RtpParameters current_parameters = media_channel_->GetRtpSendParameters(ssrc_); - RTC_DCHECK_GE(current_parameters.encodings.size(), - init_parameters_.encodings.size()); + RTC_CHECK_GE(current_parameters.encodings.size(), + init_parameters_.encodings.size()); for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { init_parameters_.encodings[i].ssrc = current_parameters.encodings[i].ssrc; @@ -650,7 +651,8 @@ void VideoRtpSender::SetSend() { break; } bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return video_media_channel()->SetVideoSend(ssrc_, &options, video_track()); + return video_media_channel()->SetVideoSend(ssrc_, &options, + video_track().get()); }); RTC_DCHECK(success); } diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index b87f8d4813..569a6007d3 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -17,6 +17,7 @@ #include #include + #include #include #include @@ -32,13 +33,16 @@ #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" #include "media/base/audio_source.h" #include "media/base/media_channel.h" #include "pc/dtmf_sender.h" #include "pc/stats_collector_interface.h" +#include "rtc_base/checks.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -222,6 +226,11 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { std::vector stream_ids_; RtpParameters init_parameters_; + // TODO(tommi): `media_channel_` and several other member variables in this + // class (ssrc_, stopped_, etc) are accessed from more than one thread without + // a guard or lock. Internally there are also several Invoke()s that we could + // remove since the upstream code may already be performing several operations + // on the worker thread. cricket::MediaChannel* media_channel_ = nullptr; rtc::scoped_refptr track_; diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc index 2c6bc7b71a..f033b37968 100644 --- a/pc/rtp_sender_receiver_unittest.cc +++ b/pc/rtp_sender_receiver_unittest.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -28,15 +29,20 @@ #include "api/rtc_error.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" #include "api/test/fake_frame_decryptor.h" #include "api/test/fake_frame_encryptor.h" #include "api/video/builtin_video_bitrate_allocator_factory.h" +#include "api/video/video_bitrate_allocator_factory.h" +#include "api/video/video_codec_constants.h" #include "media/base/codec.h" +#include "media/base/delayable.h" #include "media/base/fake_media_engine.h" #include "media/base/media_channel.h" #include "media/base/media_config.h" #include "media/base/media_engine.h" +#include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "media/base/test_utils.h" #include "media/engine/fake_webrtc_call.h" @@ -50,8 +56,6 @@ #include "pc/dtls_srtp_transport.h" #include "pc/local_audio_source.h" #include "pc/media_stream.h" -#include "pc/remote_audio_source.h" -#include "pc/rtp_receiver.h" #include "pc/rtp_sender.h" #include "pc/rtp_transport_internal.h" #include "pc/test/fake_video_track_source.h" @@ -59,11 +63,14 @@ #include "pc/video_track.h" #include "rtc_base/checks.h" #include "rtc_base/gunit.h" +#include "rtc_base/location.h" +#include "rtc_base/ref_counted_object.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" #include "test/run_loop.h" +#include "test/scoped_key_value_config.h" using ::testing::_; using ::testing::ContainerEq; @@ -174,14 +181,11 @@ class RtpSenderReceiverTest voice_channel_->SetRtpTransport(nullptr); video_channel_->SetRtpTransport(nullptr); - - channel_manager_->DestroyChannel(voice_channel_); - channel_manager_->DestroyChannel(video_channel_); } std::unique_ptr CreateDtlsSrtpTransport() { auto dtls_srtp_transport = std::make_unique( - /*rtcp_mux_required=*/true); + /*rtcp_mux_required=*/true, field_trials_); dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport_.get(), /*rtcp_dtls_transport=*/nullptr); return dtls_srtp_transport; @@ -217,7 +221,7 @@ class RtpSenderReceiverTest audio_rtp_sender_ = AudioRtpSender::Create(worker_thread_, audio_track_->id(), nullptr, set_streams_observer.get()); - ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_)); + ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); audio_rtp_sender_->SetStreams({local_stream_->id()}); audio_rtp_sender_->SetMediaChannel(voice_media_channel_); @@ -281,7 +285,7 @@ class RtpSenderReceiverTest std::make_unique(); video_rtp_sender_ = VideoRtpSender::Create( worker_thread_, video_track_->id(), set_streams_observer.get()); - ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_)); + ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); video_rtp_sender_->SetMediaChannel(video_media_channel_); @@ -347,7 +351,7 @@ class RtpSenderReceiverTest void DestroyAudioRtpReceiver() { if (!audio_rtp_receiver_) return; - audio_rtp_receiver_->Stop(); + audio_rtp_receiver_->SetMediaChannel(nullptr); audio_rtp_receiver_ = nullptr; VerifyVoiceChannelNoOutput(); } @@ -356,6 +360,7 @@ class RtpSenderReceiverTest if (!video_rtp_receiver_) return; video_rtp_receiver_->Stop(); + video_rtp_receiver_->SetMediaChannel(nullptr); video_rtp_receiver_ = nullptr; VerifyVideoChannelNoOutput(); } @@ -525,8 +530,8 @@ class RtpSenderReceiverTest cricket::FakeMediaEngine* media_engine_; std::unique_ptr channel_manager_; cricket::FakeCall fake_call_; - cricket::VoiceChannel* voice_channel_; - cricket::VideoChannel* video_channel_; + std::unique_ptr voice_channel_; + std::unique_ptr video_channel_; cricket::FakeVoiceMediaChannel* voice_media_channel_; cricket::FakeVideoMediaChannel* video_media_channel_; rtc::scoped_refptr audio_rtp_sender_; @@ -537,6 +542,7 @@ class RtpSenderReceiverTest rtc::scoped_refptr video_track_; rtc::scoped_refptr audio_track_; bool audio_sender_destroyed_signal_fired_ = false; + webrtc::test::ScopedKeyValueConfig field_trials_; }; // Test that `voice_channel_` is updated when an audio track is associated @@ -724,7 +730,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderWithoutTrackAndSsrc) { AudioTrack::Create(kAudioTrackId, nullptr); // Track but no SSRC. - EXPECT_TRUE(audio_rtp_sender_->SetTrack(track)); + EXPECT_TRUE(audio_rtp_sender_->SetTrack(track.get())); VerifyVoiceChannelNoInput(); // SSRC but no track. @@ -739,7 +745,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderWithoutTrackAndSsrc) { CreateVideoRtpSenderWithNoTrack(); // Track but no SSRC. - EXPECT_TRUE(video_rtp_sender_->SetTrack(video_track_)); + EXPECT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); VerifyVideoChannelNoInput(); // SSRC but no track. @@ -755,7 +761,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderEarlyWarmupSsrcThenTrack) { rtc::scoped_refptr track = AudioTrack::Create(kAudioTrackId, nullptr); audio_rtp_sender_->SetSsrc(kAudioSsrc); - audio_rtp_sender_->SetTrack(track); + audio_rtp_sender_->SetTrack(track.get()); VerifyVoiceChannelInput(); DestroyAudioRtpSender(); @@ -767,7 +773,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderEarlyWarmupTrackThenSsrc) { CreateAudioRtpSenderWithNoTrack(); rtc::scoped_refptr track = AudioTrack::Create(kAudioTrackId, nullptr); - audio_rtp_sender_->SetTrack(track); + audio_rtp_sender_->SetTrack(track.get()); audio_rtp_sender_->SetSsrc(kAudioSsrc); VerifyVoiceChannelInput(); @@ -780,7 +786,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupSsrcThenTrack) { AddVideoTrack(); CreateVideoRtpSenderWithNoTrack(); video_rtp_sender_->SetSsrc(kVideoSsrc); - video_rtp_sender_->SetTrack(video_track_); + video_rtp_sender_->SetTrack(video_track_.get()); VerifyVideoChannelInput(); DestroyVideoRtpSender(); @@ -791,7 +797,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupSsrcThenTrack) { TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupTrackThenSsrc) { AddVideoTrack(); CreateVideoRtpSenderWithNoTrack(); - video_rtp_sender_->SetTrack(video_track_); + video_rtp_sender_->SetTrack(video_track_.get()); video_rtp_sender_->SetSsrc(kVideoSsrc); VerifyVideoChannelInput(); @@ -894,7 +900,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderInitParametersMovedAfterNegotiation) { std::make_unique(); audio_rtp_sender_ = AudioRtpSender::Create( worker_thread_, audio_track_->id(), nullptr, set_streams_observer.get()); - ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_)); + ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); audio_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1085,7 +1091,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderInitParametersMovedAfterNegotiation) { std::make_unique(); video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(), set_streams_observer.get()); - ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_)); + ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1126,7 +1132,7 @@ TEST_F(RtpSenderReceiverTest, std::make_unique(); video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(), set_streams_observer.get()); - ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_)); + ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1157,6 +1163,44 @@ TEST_F(RtpSenderReceiverTest, DestroyVideoRtpSender(); } +#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) +using RtpSenderReceiverDeathTest = RtpSenderReceiverTest; + +TEST_F(RtpSenderReceiverDeathTest, + VideoSenderManualRemoveSimulcastFailsDeathTest) { + AddVideoTrack(false); + + std::unique_ptr set_streams_observer = + std::make_unique(); + video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(), + set_streams_observer.get()); + ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); + EXPECT_CALL(*set_streams_observer, OnSetStreams()); + video_rtp_sender_->SetStreams({local_stream_->id()}); + + std::vector init_encodings(2); + init_encodings[0].max_bitrate_bps = 60000; + init_encodings[1].max_bitrate_bps = 120000; + video_rtp_sender_->set_init_send_encodings(init_encodings); + + RtpParameters params = video_rtp_sender_->GetParameters(); + ASSERT_EQ(2u, params.encodings.size()); + EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000); + + // Simulate the setLocalDescription call as if the user used SDP munging + // to disable simulcast. + std::vector ssrcs; + ssrcs.reserve(2); + for (int i = 0; i < 2; ++i) + ssrcs.push_back(kVideoSsrcSimulcast + i); + cricket::StreamParams stream_params = + cricket::StreamParams::CreateLegacy(kVideoSsrc); + video_media_channel_->AddSendStream(stream_params); + video_rtp_sender_->SetMediaChannel(video_media_channel_); + EXPECT_DEATH(video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast), ""); +} +#endif + TEST_F(RtpSenderReceiverTest, VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) { video_rtp_sender_ = @@ -1515,7 +1559,7 @@ TEST_F(RtpSenderReceiverTest, video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed); video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(), set_streams_observer.get()); - ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_)); + ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); video_rtp_sender_->SetStreams({local_stream_->id()}); video_rtp_sender_->SetMediaChannel(video_media_channel_); @@ -1626,7 +1670,7 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCannotSetFrameEncryptorAfterStop) { TEST_F(RtpSenderReceiverTest, AudioReceiverCanSetFrameDecryptor) { CreateAudioRtpReceiver(); rtc::scoped_refptr fake_frame_decryptor( - new FakeFrameDecryptor()); + rtc::make_ref_counted()); EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); EXPECT_EQ(fake_frame_decryptor.get(), @@ -1638,9 +1682,9 @@ TEST_F(RtpSenderReceiverTest, AudioReceiverCanSetFrameDecryptor) { TEST_F(RtpSenderReceiverTest, AudioReceiverCannotSetFrameDecryptorAfterStop) { CreateAudioRtpReceiver(); rtc::scoped_refptr fake_frame_decryptor( - new FakeFrameDecryptor()); + rtc::make_ref_counted()); EXPECT_EQ(nullptr, audio_rtp_receiver_->GetFrameDecryptor()); - audio_rtp_receiver_->Stop(); + audio_rtp_receiver_->SetMediaChannel(nullptr); audio_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyAudioRtpReceiver(); @@ -1673,7 +1717,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCannotSetFrameEncryptorAfterStop) { TEST_F(RtpSenderReceiverTest, VideoReceiverCanSetFrameDecryptor) { CreateVideoRtpReceiver(); rtc::scoped_refptr fake_frame_decryptor( - new FakeFrameDecryptor()); + rtc::make_ref_counted()); EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); EXPECT_EQ(fake_frame_decryptor.get(), @@ -1685,9 +1729,9 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCanSetFrameDecryptor) { TEST_F(RtpSenderReceiverTest, VideoReceiverCannotSetFrameDecryptorAfterStop) { CreateVideoRtpReceiver(); rtc::scoped_refptr fake_frame_decryptor( - new FakeFrameDecryptor()); + rtc::make_ref_counted()); EXPECT_EQ(nullptr, video_rtp_receiver_->GetFrameDecryptor()); - video_rtp_receiver_->Stop(); + video_rtp_receiver_->SetMediaChannel(nullptr); video_rtp_receiver_->SetFrameDecryptor(fake_frame_decryptor); // TODO(webrtc:9926) - Validate media channel not set once fakes updated. DestroyVideoRtpReceiver(); diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index c8afec899a..caa9ba5d64 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -10,20 +10,24 @@ #include "pc/rtp_transceiver.h" +#include #include #include #include #include #include "absl/algorithm/container.h" +#include "api/peer_connection_interface.h" #include "api/rtp_parameters.h" #include "api/sequence_checker.h" #include "media/base/codec.h" #include "media/base/media_constants.h" +#include "pc/channel.h" #include "pc/channel_manager.h" #include "pc/rtp_media_utils.h" #include "pc/session_description.h" #include "rtc_base/checks.h" +#include "rtc_base/location.h" #include "rtc_base/logging.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread.h" @@ -155,34 +159,71 @@ RtpTransceiver::~RtpTransceiver() { StopInternal(); } - RTC_CHECK(!channel_) << "Missing call to SetChannel(nullptr)?"; + RTC_CHECK(!channel_) << "Missing call to ClearChannel?"; +} + +RTCError RtpTransceiver::CreateChannel( + absl::string_view mid, + Call* call_ptr, + const cricket::MediaConfig& media_config, + bool srtp_required, + CryptoOptions crypto_options, + const cricket::AudioOptions& audio_options, + const cricket::VideoOptions& video_options, + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + std::function transport_lookup) { + RTC_DCHECK_RUN_ON(thread_); + if (!channel_manager_->media_engine()) { + // TODO(hta): Must be a better way + return RTCError(RTCErrorType::INTERNAL_ERROR, + "No media engine for mid=" + std::string(mid)); + } + std::unique_ptr new_channel; + if (media_type() == cricket::MEDIA_TYPE_AUDIO) { + // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to + // the worker thread. We shouldn't be using the `call_ptr_` hack here but + // simply be on the worker thread and use `call_` (update upstream code). + new_channel = channel_manager_->CreateVoiceChannel( + call_ptr, media_config, mid, srtp_required, crypto_options, + audio_options); + + } else { + RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, media_type()); + + // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to + // the worker thread. We shouldn't be using the `call_ptr_` hack here but + // simply be on the worker thread and use `call_` (update upstream code). + new_channel = channel_manager_->CreateVideoChannel( + call_ptr, media_config, mid, srtp_required, crypto_options, + video_options, video_bitrate_allocator_factory); + } + if (!new_channel) { + // TODO(hta): Must be a better way + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to create channel for mid=" + std::string(mid)); + } + SetChannel(std::move(new_channel), transport_lookup); + return RTCError::OK(); } void RtpTransceiver::SetChannel( - cricket::ChannelInterface* channel, + std::unique_ptr channel, std::function transport_lookup) { RTC_DCHECK_RUN_ON(thread_); - // Cannot set a non-null channel on a stopped transceiver. - if ((stopped_ && channel) || channel == channel_) { + RTC_DCHECK(channel); + RTC_DCHECK(transport_lookup); + RTC_DCHECK(!channel_); + // Cannot set a channel on a stopped transceiver. + if (stopped_) { return; } - RTC_DCHECK(channel || channel_); - RTC_DCHECK(!channel || transport_lookup) << "lookup function not supplied"; - RTC_LOG_THREAD_BLOCK_COUNT(); - if (channel_) { - signaling_thread_safety_->SetNotAlive(); - signaling_thread_safety_ = nullptr; - } - - if (channel) { - RTC_DCHECK_EQ(media_type(), channel->media_type()); - signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); - } + RTC_DCHECK_EQ(media_type(), channel->media_type()); + signaling_thread_safety_ = PendingTaskSafetyFlag::Create(); - cricket::ChannelInterface* channel_to_delete = nullptr; + std::unique_ptr channel_to_delete; // An alternative to this, could be to require SetChannel to be called // on the network thread. The channel object operates for the most part @@ -197,41 +238,76 @@ void RtpTransceiver::SetChannel( if (channel_) { channel_->SetFirstPacketReceivedCallback(nullptr); channel_->SetRtpTransport(nullptr); - channel_to_delete = channel_; + channel_to_delete = std::move(channel_); } - channel_ = channel; + channel_ = std::move(channel); - if (channel_) { - channel_->SetRtpTransport(transport_lookup(channel_->mid())); - channel_->SetFirstPacketReceivedCallback( - [thread = thread_, flag = signaling_thread_safety_, this]() mutable { - thread->PostTask(ToQueuedTask( - std::move(flag), [this]() { OnFirstPacketReceived(); })); - }); - } + channel_->SetRtpTransport(transport_lookup(channel_->mid())); + channel_->SetFirstPacketReceivedCallback( + [thread = thread_, flag = signaling_thread_safety_, this]() mutable { + thread->PostTask(ToQueuedTask(std::move(flag), + [this]() { OnFirstPacketReceived(); })); + }); }); + PushNewMediaChannelAndDeleteChannel(nullptr); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); +} + +void RtpTransceiver::ClearChannel() { + RTC_DCHECK_RUN_ON(thread_); - for (const auto& sender : senders_) { - sender->internal()->SetMediaChannel(channel_ ? channel_->media_channel() - : nullptr); + if (!channel_) { + return; } - RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + RTC_LOG_THREAD_BLOCK_COUNT(); - for (const auto& receiver : receivers_) { - if (!channel_) { - receiver->internal()->Stop(); - } else { - receiver->internal()->SetMediaChannel(channel_->media_channel()); - } + if (channel_) { + signaling_thread_safety_->SetNotAlive(); + signaling_thread_safety_ = nullptr; } + std::unique_ptr channel_to_delete; + + channel_manager_->network_thread()->Invoke(RTC_FROM_HERE, [&]() { + if (channel_) { + channel_->SetFirstPacketReceivedCallback(nullptr); + channel_->SetRtpTransport(nullptr); + channel_to_delete = std::move(channel_); + } + }); + + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(1); + PushNewMediaChannelAndDeleteChannel(std::move(channel_to_delete)); - // Destroy the channel, if we had one, now _after_ updating the receivers who - // might have had references to the previous channel. - if (channel_to_delete) { - channel_manager_->DestroyChannel(channel_to_delete); + RTC_DCHECK_BLOCK_COUNT_NO_MORE_THAN(2); +} + +void RtpTransceiver::PushNewMediaChannelAndDeleteChannel( + std::unique_ptr channel_to_delete) { + // The clumsy combination of pushing down media channel and deleting + // the channel is due to the desire to do both things in one Invoke(). + if (!channel_to_delete && senders_.empty() && receivers_.empty()) { + return; } + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + // Push down the new media_channel, if any, otherwise clear it. + auto* media_channel = channel_ ? channel_->media_channel() : nullptr; + for (const auto& sender : senders_) { + sender->internal()->SetMediaChannel(media_channel); + } + + for (const auto& receiver : receivers_) { + receiver->internal()->SetMediaChannel(media_channel); + } + + // Destroy the channel, if we had one, now _after_ updating the receivers + // who might have had references to the previous channel. + if (channel_to_delete) { + channel_to_delete.reset(nullptr); + } + }); } void RtpTransceiver::AddSender( @@ -272,6 +348,7 @@ void RtpTransceiver::AddReceiver( } bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { + RTC_DCHECK_RUN_ON(thread_); RTC_DCHECK(!unified_plan_); if (receiver) { RTC_DCHECK_EQ(media_type(), receiver->media_type()); @@ -280,8 +357,13 @@ bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) { if (it == receivers_.end()) { return false; } - // `Stop()` will clear the internally cached pointer to the media channel. + (*it)->internal()->Stop(); + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + // `Stop()` will clear the receiver's pointer to the media channel. + (*it)->internal()->SetMediaChannel(nullptr); + }); + receivers_.erase(it); return true; } @@ -339,7 +421,8 @@ void RtpTransceiver::set_current_direction(RtpTransceiverDirection direction) { } } -void RtpTransceiver::set_fired_direction(RtpTransceiverDirection direction) { +void RtpTransceiver::set_fired_direction( + absl::optional direction) { fired_direction_ = direction; } @@ -399,15 +482,22 @@ void RtpTransceiver::StopSendingAndReceiving() { // // 3. Stop sending media with sender. // + RTC_DCHECK_RUN_ON(thread_); + // 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as // specified in [RFC3550]. - RTC_DCHECK_RUN_ON(thread_); for (const auto& sender : senders_) sender->internal()->Stop(); - // 5. Stop receiving media with receiver. + // Signal to receiver sources that we're stopping. for (const auto& receiver : receivers_) - receiver->internal()->StopAndEndTrack(); + receiver->internal()->Stop(); + + channel_manager_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + // 5 Stop receiving media with receiver. + for (const auto& receiver : receivers_) + receiver->internal()->SetMediaChannel(nullptr); + }); stopping_ = true; direction_ = webrtc::RtpTransceiverDirection::kInactive; diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h index b8dbb677dd..b61ca752d1 100644 --- a/pc/rtp_transceiver.h +++ b/pc/rtp_transceiver.h @@ -13,34 +13,46 @@ #include -#include #include +#include #include #include #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/audio_options.h" +#include "api/jsep.h" #include "api/media_types.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" #include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" #include "api/task_queue/task_queue_base.h" +#include "api/video/video_bitrate_allocator_factory.h" +#include "media/base/media_channel.h" #include "pc/channel_interface.h" -#include "pc/channel_manager.h" #include "pc/proxy.h" #include "pc/rtp_receiver.h" #include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" #include "pc/rtp_sender_proxy.h" -#include "rtc_base/ref_counted_object.h" +#include "pc/rtp_transport_internal.h" +#include "pc/session_description.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread_annotations.h" +namespace cricket { +class ChannelManager; +} + namespace webrtc { +class PeerConnectionSdpMethods; + // Implementation of the public RtpTransceiverInterface. // // The RtpTransceiverInterface is only intended to be used with a PeerConnection @@ -62,18 +74,15 @@ namespace webrtc { // with this m= section. Since the transceiver, senders, and receivers are // reference counted and can be referenced from JavaScript (in Chromium), these // objects must be ready to live for an arbitrary amount of time. The -// BaseChannel is not reference counted and is owned by the ChannelManager, so -// the PeerConnection must take care of creating/deleting the BaseChannel and -// setting the channel reference in the transceiver to null when it has been -// deleted. +// BaseChannel is not reference counted, so +// the PeerConnection must take care of creating/deleting the BaseChannel. // // The RtpTransceiver is specialized to either audio or video according to the // MediaType specified in the constructor. Audio RtpTransceivers will have // AudioRtpSenders, AudioRtpReceivers, and a VoiceChannel. Video RtpTransceivers // will have VideoRtpSenders, VideoRtpReceivers, and a VideoChannel. -class RtpTransceiver final - : public rtc::RefCountedObject, - public sigslot::has_slots<> { +class RtpTransceiver : public RtpTransceiverInterface, + public sigslot::has_slots<> { public: // Construct a Plan B-style RtpTransceiver with no senders, receivers, or // channel set. @@ -97,25 +106,33 @@ class RtpTransceiver final // Returns the Voice/VideoChannel set for this transceiver. May be null if // the transceiver is not in the currently set local/remote description. - cricket::ChannelInterface* channel() const { return channel_; } + cricket::ChannelInterface* channel() const { return channel_.get(); } + + // Creates the Voice/VideoChannel and sets it. + RTCError CreateChannel( + absl::string_view mid, + Call* call_ptr, + const cricket::MediaConfig& media_config, + bool srtp_required, + CryptoOptions crypto_options, + const cricket::AudioOptions& audio_options, + const cricket::VideoOptions& video_options, + VideoBitrateAllocatorFactory* video_bitrate_allocator_factory, + std::function transport_lookup); // Sets the Voice/VideoChannel. The caller must pass in the correct channel // implementation based on the type of the transceiver. The call must // furthermore be made on the signaling thread. // // `channel`: The channel instance to be associated with the transceiver. - // When a valid pointer is passed for `channel`, the state of the object + // This must be a valid pointer. + // The state of the object // is expected to be newly constructed and not initalized for network // activity (see next parameter for more). // - // NOTE: For all practical purposes, the ownership of the channel - // object should be considered to lie with the transceiver until - // `SetChannel()` is called again with nullptr set as the new channel. - // Moving forward, this parameter will change to either be a - // std::unique_ptr<> or the full construction of the channel object will - // be moved to happen within the context of the transceiver class. + // The transceiver takes ownership of `channel`. // - // `transport_lookup`: When `channel` points to a valid channel object, this + // `transport_lookup`: This // callback function will be used to look up the `RtpTransport` object // to associate with the channel via `BaseChannel::SetRtpTransport`. // The lookup function will be called on the network thread, synchronously @@ -127,10 +144,14 @@ class RtpTransceiver final // synchronously to the network thread from the signaling thread. // The callback allows us to combine the transport lookup with network // state initialization of the channel object. - void SetChannel(cricket::ChannelInterface* channel, + // ClearChannel() must be used before calling SetChannel() again. + void SetChannel(std::unique_ptr channel, std::function transport_lookup); + // Clear the association between the transceiver and the channel. + void ClearChannel(); + // Adds an RtpSender of the appropriate type to be owned by this transceiver. // Must not be null. void AddSender( @@ -198,8 +219,8 @@ class RtpTransceiver final // Sets the fired direction for this transceiver. The fired direction is null // until SetRemoteDescription is called or an answer is set (either local or - // remote). - void set_fired_direction(RtpTransceiverDirection direction); + // remote) after which the only valid reason to go back to null is rollback. + void set_fired_direction(absl::optional direction); // According to JSEP rules for SetRemoteDescription, RtpTransceivers can be // reused only if they were added by AddTrack. @@ -274,6 +295,10 @@ class RtpTransceiver final private: void OnFirstPacketReceived(); void StopSendingAndReceiving(); + // Delete a channel, and ensure that references to its media channel + // are updated before deleting it. + void PushNewMediaChannelAndDeleteChannel( + std::unique_ptr channel_to_delete); // Enforce that this object is created, used and destroyed on one thread. TaskQueueBase* const thread_; @@ -298,7 +323,10 @@ class RtpTransceiver final bool reused_for_addtrack_ = false; bool has_ever_been_used_to_send_ = false; - cricket::ChannelInterface* channel_ = nullptr; + // Accessed on both thread_ and the network thread. Considered safe + // because all access on the network thread is within an invoke() + // from thread_. + std::unique_ptr channel_ = nullptr; cricket::ChannelManager* channel_manager_ = nullptr; std::vector codec_preferences_; std::vector header_extensions_to_offer_; diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc index d63af92169..ce2eefc974 100644 --- a/pc/rtp_transceiver_unittest.cc +++ b/pc/rtp_transceiver_unittest.cc @@ -13,13 +13,18 @@ #include "pc/rtp_transceiver.h" #include +#include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtp_parameters.h" #include "media/base/fake_media_engine.h" +#include "media/base/media_engine.h" +#include "pc/channel_manager.h" #include "pc/test/mock_channel_interface.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" +#include "rtc_base/thread.h" #include "test/gmock.h" #include "test/gtest.h" @@ -40,8 +45,6 @@ class ChannelManagerForTest : public cricket::ChannelManager { true, rtc::Thread::Current(), rtc::Thread::Current()) {} - - MOCK_METHOD(void, DestroyChannel, (cricket::ChannelInterface*), (override)); }; } // namespace @@ -49,80 +52,82 @@ class ChannelManagerForTest : public cricket::ChannelManager { TEST(RtpTransceiverTest, CannotSetChannelOnStoppedTransceiver) { ChannelManagerForTest cm; const std::string content_name("my_mid"); - RtpTransceiver transceiver(cricket::MediaType::MEDIA_TYPE_AUDIO, &cm); - cricket::MockChannelInterface channel1; - EXPECT_CALL(channel1, media_type()) + auto transceiver = rtc::make_ref_counted( + cricket::MediaType::MEDIA_TYPE_AUDIO, &cm); + auto channel1 = std::make_unique(); + EXPECT_CALL(*channel1, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); - EXPECT_CALL(channel1, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(channel1, SetFirstPacketReceivedCallback(_)); - EXPECT_CALL(channel1, SetRtpTransport(_)).WillRepeatedly(Return(true)); - - transceiver.SetChannel(&channel1, [&](const std::string& mid) { + EXPECT_CALL(*channel1, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(*channel1, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(*channel1, SetRtpTransport(_)).WillRepeatedly(Return(true)); + auto channel1_ptr = channel1.get(); + transceiver->SetChannel(std::move(channel1), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); - EXPECT_EQ(&channel1, transceiver.channel()); + EXPECT_EQ(channel1_ptr, transceiver->channel()); // Stop the transceiver. - transceiver.StopInternal(); - EXPECT_EQ(&channel1, transceiver.channel()); + transceiver->StopInternal(); + EXPECT_EQ(channel1_ptr, transceiver->channel()); - cricket::MockChannelInterface channel2; - EXPECT_CALL(channel2, media_type()) + auto channel2 = std::make_unique(); + EXPECT_CALL(*channel2, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); + // Clear the current channel - required to allow SetChannel() + EXPECT_CALL(*channel1_ptr, SetFirstPacketReceivedCallback(_)); + transceiver->ClearChannel(); // Channel can no longer be set, so this call should be a no-op. - transceiver.SetChannel(&channel2, [](const std::string&) { return nullptr; }); - EXPECT_EQ(&channel1, transceiver.channel()); - - // Clear the current channel before `transceiver` goes out of scope. - EXPECT_CALL(channel1, SetFirstPacketReceivedCallback(_)); - EXPECT_CALL(cm, DestroyChannel(&channel1)).WillRepeatedly(testing::Return()); - transceiver.SetChannel(nullptr, nullptr); + transceiver->SetChannel(std::move(channel2), + [](const std::string&) { return nullptr; }); + EXPECT_EQ(nullptr, transceiver->channel()); } // Checks that a channel can be unset on a stopped `RtpTransceiver` TEST(RtpTransceiverTest, CanUnsetChannelOnStoppedTransceiver) { ChannelManagerForTest cm; const std::string content_name("my_mid"); - RtpTransceiver transceiver(cricket::MediaType::MEDIA_TYPE_VIDEO, &cm); - cricket::MockChannelInterface channel; - EXPECT_CALL(channel, media_type()) + auto transceiver = rtc::make_ref_counted( + cricket::MediaType::MEDIA_TYPE_VIDEO, &cm); + auto channel = std::make_unique(); + EXPECT_CALL(*channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_VIDEO)); - EXPECT_CALL(channel, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(channel, SetFirstPacketReceivedCallback(_)) + EXPECT_CALL(*channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(*channel, SetFirstPacketReceivedCallback(_)) .WillRepeatedly(testing::Return()); - EXPECT_CALL(channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); - EXPECT_CALL(cm, DestroyChannel(&channel)).WillRepeatedly(testing::Return()); + EXPECT_CALL(*channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); - transceiver.SetChannel(&channel, [&](const std::string& mid) { + auto channel_ptr = channel.get(); + transceiver->SetChannel(std::move(channel), [&](const std::string& mid) { EXPECT_EQ(mid, content_name); return nullptr; }); - EXPECT_EQ(&channel, transceiver.channel()); + EXPECT_EQ(channel_ptr, transceiver->channel()); // Stop the transceiver. - transceiver.StopInternal(); - EXPECT_EQ(&channel, transceiver.channel()); + transceiver->StopInternal(); + EXPECT_EQ(channel_ptr, transceiver->channel()); // Set the channel to `nullptr`. - transceiver.SetChannel(nullptr, nullptr); - EXPECT_EQ(nullptr, transceiver.channel()); + transceiver->ClearChannel(); + EXPECT_EQ(nullptr, transceiver->channel()); } class RtpTransceiverUnifiedPlanTest : public ::testing::Test { public: RtpTransceiverUnifiedPlanTest() - : transceiver_(RtpSenderProxyWithInternal::Create( - rtc::Thread::Current(), - sender_), - RtpReceiverProxyWithInternal::Create( - rtc::Thread::Current(), - rtc::Thread::Current(), - receiver_), - &channel_manager_, - channel_manager_.GetSupportedAudioRtpHeaderExtensions(), - /* on_negotiation_needed= */ [] {}) {} + : transceiver_(rtc::make_ref_counted( + RtpSenderProxyWithInternal::Create( + rtc::Thread::Current(), + sender_), + RtpReceiverProxyWithInternal::Create( + rtc::Thread::Current(), + rtc::Thread::Current(), + receiver_), + &channel_manager_, + channel_manager_.GetSupportedAudioRtpHeaderExtensions(), + /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); @@ -141,25 +146,26 @@ class RtpTransceiverUnifiedPlanTest : public ::testing::Test { rtc::scoped_refptr receiver_ = MockReceiver(); rtc::scoped_refptr sender_ = MockSender(); ChannelManagerForTest channel_manager_; - RtpTransceiver transceiver_; + rtc::scoped_refptr transceiver_; }; // Basic tests for Stop() TEST_F(RtpTransceiverUnifiedPlanTest, StopSetsDirection) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver_.direction()); - EXPECT_FALSE(transceiver_.current_direction()); - transceiver_.StopStandard(); - EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_.direction()); - EXPECT_FALSE(transceiver_.current_direction()); - transceiver_.StopTransceiverProcedure(); - EXPECT_TRUE(transceiver_.current_direction()); - EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_.direction()); + EXPECT_EQ(RtpTransceiverDirection::kInactive, transceiver_->direction()); + EXPECT_FALSE(transceiver_->current_direction()); + transceiver_->StopStandard(); + EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); + EXPECT_FALSE(transceiver_->current_direction()); + transceiver_->StopTransceiverProcedure(); + EXPECT_TRUE(transceiver_->current_direction()); + EXPECT_EQ(RtpTransceiverDirection::kStopped, transceiver_->direction()); EXPECT_EQ(RtpTransceiverDirection::kStopped, - *transceiver_.current_direction()); + *transceiver_->current_direction()); } class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { @@ -178,16 +184,17 @@ class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { RtpHeaderExtensionCapability(RtpExtension::kVideoRotationUri, 4, RtpTransceiverDirection::kSendRecv)}), - transceiver_(RtpSenderProxyWithInternal::Create( - rtc::Thread::Current(), - sender_), - RtpReceiverProxyWithInternal::Create( - rtc::Thread::Current(), - rtc::Thread::Current(), - receiver_), - &channel_manager_, - extensions_, - /* on_negotiation_needed= */ [] {}) {} + transceiver_(rtc::make_ref_counted( + RtpSenderProxyWithInternal::Create( + rtc::Thread::Current(), + sender_), + RtpReceiverProxyWithInternal::Create( + rtc::Thread::Current(), + rtc::Thread::Current(), + receiver_), + &channel_manager_, + extensions_, + /* on_negotiation_needed= */ [] {})) {} static rtc::scoped_refptr MockReceiver() { auto receiver = rtc::make_ref_counted(); @@ -203,13 +210,9 @@ class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { return sender; } - void ClearChannel(cricket::MockChannelInterface& mock_channel) { - EXPECT_CALL(*sender_.get(), SetMediaChannel(nullptr)); - EXPECT_CALL(*receiver_.get(), Stop()); - EXPECT_CALL(mock_channel, SetFirstPacketReceivedCallback(_)); - EXPECT_CALL(channel_manager_, DestroyChannel(&mock_channel)) - .WillRepeatedly(testing::Return()); - transceiver_.SetChannel(nullptr, nullptr); + void ClearChannel() { + EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); + transceiver_->ClearChannel(); } rtc::scoped_refptr receiver_ = MockReceiver(); @@ -217,152 +220,163 @@ class RtpTransceiverTestForHeaderExtensions : public ::testing::Test { ChannelManagerForTest channel_manager_; std::vector extensions_; - RtpTransceiver transceiver_; + rtc::scoped_refptr transceiver_; }; TEST_F(RtpTransceiverTestForHeaderExtensions, OffersChannelManagerList) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, ModifiesDirection) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kSendOnly; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kRecvOnly; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kSendRecv; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); modified_extensions[0].direction = RtpTransceiverDirection::kInactive; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, AcceptsStoppedExtension) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); auto modified_extensions = extensions_; modified_extensions[0].direction = RtpTransceiverDirection::kStopped; EXPECT_TRUE( - transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions).ok()); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), modified_extensions); + transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions).ok()); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), modified_extensions); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsUnsupportedExtension) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); std::vector modified_extensions( {RtpHeaderExtensionCapability("uri3", 1, RtpTransceiverDirection::kSendRecv)}); - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::UNSUPPORTED_PARAMETER)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, RejectsStoppedMandatoryExtensions) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); std::vector modified_extensions = extensions_; // Attempting to stop the mandatory MID extension. modified_extensions[2].direction = RtpTransceiverDirection::kStopped; - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); modified_extensions = extensions_; // Attempting to stop the mandatory video orientation extension. modified_extensions[3].direction = RtpTransceiverDirection::kStopped; - EXPECT_THAT(transceiver_.SetOfferedRtpHeaderExtensions(modified_extensions), + EXPECT_THAT(transceiver_->SetOfferedRtpHeaderExtensions(modified_extensions), Property(&RTCError::type, RTCErrorType::INVALID_MODIFICATION)); - EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_); + EXPECT_EQ(transceiver_->HeaderExtensionsToOffer(), extensions_); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithoutChannel) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre()); } TEST_F(RtpTransceiverTestForHeaderExtensions, NoNegotiatedHdrExtsWithChannelWithoutNegotiation) { const std::string content_name("my_mid"); - EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - cricket::MockChannelInterface mock_channel; - EXPECT_CALL(mock_channel, SetFirstPacketReceivedCallback(_)); - EXPECT_CALL(mock_channel, media_type()) + auto mock_channel = std::make_unique(); + auto mock_channel_ptr = mock_channel.get(); + EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(*mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); - EXPECT_CALL(mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); - EXPECT_CALL(mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); - transceiver_.SetChannel(&mock_channel, - [](const std::string&) { return nullptr; }); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre()); - - ClearChannel(mock_channel); + EXPECT_CALL(*mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); + transceiver_->SetChannel(std::move(mock_channel), + [](const std::string&) { return nullptr; }); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre()); + + EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_)); + ClearChannel(); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) { const std::string content_name("my_mid"); - EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver_.get(), Stop()).WillRepeatedly(Return()); EXPECT_CALL(*sender_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); - cricket::MockChannelInterface mock_channel; - EXPECT_CALL(mock_channel, SetFirstPacketReceivedCallback(_)); - EXPECT_CALL(mock_channel, media_type()) + auto mock_channel = std::make_unique(); + auto mock_channel_ptr = mock_channel.get(); + EXPECT_CALL(*mock_channel, SetFirstPacketReceivedCallback(_)); + EXPECT_CALL(*mock_channel, media_type()) .WillRepeatedly(Return(cricket::MediaType::MEDIA_TYPE_AUDIO)); - EXPECT_CALL(mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); - EXPECT_CALL(mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); - EXPECT_CALL(mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(*mock_channel, media_channel()).WillRepeatedly(Return(nullptr)); + EXPECT_CALL(*mock_channel, mid()).WillRepeatedly(ReturnRef(content_name)); + EXPECT_CALL(*mock_channel, SetRtpTransport(_)).WillRepeatedly(Return(true)); cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1), webrtc::RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - transceiver_.SetChannel(&mock_channel, - [](const std::string&) { return nullptr; }); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + transceiver_->SetChannel(std::move(mock_channel), + [](const std::string&) { return nullptr; }); + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri1", 1, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( "uri2", 2, RtpTransceiverDirection::kSendRecv))); - ClearChannel(mock_channel); + EXPECT_CALL(*mock_channel_ptr, SetFirstPacketReceivedCallback(_)); + ClearChannel(); } TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExtsSecondTime) { - EXPECT_CALL(*receiver_.get(), StopAndEndTrack()); + EXPECT_CALL(*receiver_.get(), Stop()); + EXPECT_CALL(*receiver_.get(), SetMediaChannel(_)); EXPECT_CALL(*sender_.get(), SetTransceiverAsStopped()); EXPECT_CALL(*sender_.get(), Stop()); @@ -370,9 +384,9 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, webrtc::RtpExtension("uri2", 2)}; cricket::AudioContentDescription description; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri1", 1, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( @@ -381,9 +395,9 @@ TEST_F(RtpTransceiverTestForHeaderExtensions, extensions = {webrtc::RtpExtension("uri3", 4), webrtc::RtpExtension("uri5", 6)}; description.set_rtp_header_extensions(extensions); - transceiver_.OnNegotiationUpdate(SdpType::kAnswer, &description); + transceiver_->OnNegotiationUpdate(SdpType::kAnswer, &description); - EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), + EXPECT_THAT(transceiver_->HeaderExtensionsNegotiated(), ElementsAre(RtpHeaderExtensionCapability( "uri3", 4, RtpTransceiverDirection::kSendRecv), RtpHeaderExtensionCapability( diff --git a/pc/rtp_transmission_manager.cc b/pc/rtp_transmission_manager.cc index 951d1c38e5..6939a07dec 100644 --- a/pc/rtp_transmission_manager.cc +++ b/pc/rtp_transmission_manager.cc @@ -10,19 +10,21 @@ #include "pc/rtp_transmission_manager.h" -#include +#include #include #include "absl/types/optional.h" #include "api/peer_connection_interface.h" #include "api/rtp_transceiver_direction.h" #include "pc/audio_rtp_receiver.h" -#include "pc/channel.h" +#include "pc/channel_interface.h" +#include "pc/channel_manager.h" #include "pc/stats_collector_interface.h" #include "pc/video_rtp_receiver.h" #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -80,10 +82,9 @@ cricket::VoiceMediaChannel* RtpTransmissionManager::voice_media_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* voice_channel = static_cast( - GetAudioTransceiver()->internal()->channel()); + auto* voice_channel = GetAudioTransceiver()->internal()->channel(); if (voice_channel) { - return voice_channel->media_channel(); + return voice_channel->voice_media_channel(); } else { return nullptr; } @@ -93,10 +94,9 @@ cricket::VideoMediaChannel* RtpTransmissionManager::video_media_channel() const { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(!IsUnifiedPlan()); - auto* video_channel = static_cast( - GetVideoTransceiver()->internal()->channel()); + auto* video_channel = GetVideoTransceiver()->internal()->channel(); if (video_channel) { - return video_channel->media_channel(); + return video_channel->video_media_channel(); } else { return nullptr; } @@ -176,7 +176,7 @@ RtpTransmissionManager::AddTrackUnifiedPlan( transceiver->internal()->set_direction( RtpTransceiverDirection::kSendOnly); } - transceiver->sender()->SetTrack(track); + transceiver->sender()->SetTrack(track.get()); transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); transceiver->internal()->set_reused_for_addtrack(true); } else { @@ -226,7 +226,7 @@ RtpTransmissionManager::CreateSender( signaling_thread(), VideoRtpSender::Create(worker_thread(), id, this)); NoteUsageEvent(UsageEvent::VIDEO_ADDED); } - bool set_track_succeeded = sender->SetTrack(track); + bool set_track_succeeded = sender->SetTrack(track.get()); RTC_DCHECK(set_track_succeeded); sender->internal()->set_stream_ids(stream_ids); sender->internal()->set_init_send_encodings(send_encodings); @@ -270,7 +270,7 @@ RtpTransmissionManager::CreateAndAddTransceiver( RTC_DCHECK(!FindSenderById(sender->id())); auto transceiver = RtpTransceiverProxyWithInternal::Create( signaling_thread(), - new RtpTransceiver( + rtc::make_ref_counted( sender, receiver, channel_manager(), sender->media_type() == cricket::MEDIA_TYPE_AUDIO ? channel_manager()->GetSupportedAudioRtpHeaderExtensions() @@ -408,7 +408,7 @@ void RtpTransmissionManager::RemoveAudioTrack(AudioTrackInterface* track, << " doesn't exist."; return; } - GetAudioTransceiver()->internal()->RemoveSender(sender); + GetAudioTransceiver()->internal()->RemoveSender(sender.get()); } void RtpTransmissionManager::AddVideoTrack(VideoTrackInterface* track, @@ -447,7 +447,7 @@ void RtpTransmissionManager::RemoveVideoTrack(VideoTrackInterface* track, << " doesn't exist."; return; } - GetVideoTransceiver()->internal()->RemoveSender(sender); + GetVideoTransceiver()->internal()->RemoveSender(sender.get()); } void RtpTransmissionManager::CreateAudioReceiver( @@ -459,13 +459,14 @@ void RtpTransmissionManager::CreateAudioReceiver( // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use // the constructor taking stream IDs instead. auto audio_receiver = rtc::make_ref_counted( - worker_thread(), remote_sender_info.sender_id, streams, IsUnifiedPlan()); - audio_receiver->SetMediaChannel(voice_media_channel()); + worker_thread(), remote_sender_info.sender_id, streams, IsUnifiedPlan(), + voice_media_channel()); if (remote_sender_info.sender_id == kDefaultAudioSenderId) { audio_receiver->SetupUnsignaledMediaChannel(); } else { audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); } + auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(audio_receiver)); GetAudioTransceiver()->internal()->AddReceiver(receiver); @@ -483,12 +484,13 @@ void RtpTransmissionManager::CreateVideoReceiver( // the constructor taking stream IDs instead. auto video_receiver = rtc::make_ref_counted( worker_thread(), remote_sender_info.sender_id, streams); - video_receiver->SetMediaChannel(video_media_channel()); - if (remote_sender_info.sender_id == kDefaultVideoSenderId) { - video_receiver->SetupUnsignaledMediaChannel(); - } else { - video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } + + video_receiver->SetupMediaChannel( + remote_sender_info.sender_id == kDefaultVideoSenderId + ? absl::nullopt + : absl::optional(remote_sender_info.first_ssrc), + video_media_channel()); + auto receiver = RtpReceiverProxyWithInternal::Create( signaling_thread(), worker_thread(), std::move(video_receiver)); GetVideoTransceiver()->internal()->AddReceiver(receiver); @@ -508,9 +510,9 @@ RtpTransmissionManager::RemoveAndStopReceiver( return nullptr; } if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - GetAudioTransceiver()->internal()->RemoveReceiver(receiver); + GetAudioTransceiver()->internal()->RemoveReceiver(receiver.get()); } else { - GetVideoTransceiver()->internal()->RemoveReceiver(receiver); + GetVideoTransceiver()->internal()->RemoveReceiver(receiver.get()); } return receiver; } diff --git a/pc/rtp_transmission_manager.h b/pc/rtp_transmission_manager.h index 209c8408de..6e25f2d9b7 100644 --- a/pc/rtp_transmission_manager.h +++ b/pc/rtp_transmission_manager.h @@ -27,9 +27,10 @@ #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "media/base/media_channel.h" -#include "pc/channel_manager.h" #include "pc/rtp_receiver.h" +#include "pc/rtp_receiver_proxy.h" #include "pc/rtp_sender.h" +#include "pc/rtp_sender_proxy.h" #include "pc/rtp_transceiver.h" #include "pc/stats_collector_interface.h" #include "pc/transceiver_list.h" @@ -39,6 +40,10 @@ #include "rtc_base/thread_annotations.h" #include "rtc_base/weak_ptr.h" +namespace cricket { +class ChannelManager; +} + namespace rtc { class Thread; } @@ -253,9 +258,9 @@ class RtpTransmissionManager : public RtpSenderBase::SetStreamsObserver { bool closed_ = false; bool const is_unified_plan_; - rtc::Thread* signaling_thread_; - rtc::Thread* worker_thread_; - cricket::ChannelManager* channel_manager_; + rtc::Thread* const signaling_thread_; + rtc::Thread* const worker_thread_; + cricket::ChannelManager* const channel_manager_; UsagePattern* usage_pattern_; PeerConnectionObserver* observer_; StatsCollectorInterface* const stats_; diff --git a/pc/rtp_transport.cc b/pc/rtp_transport.cc index d4edb9501c..334dc4d0b2 100644 --- a/pc/rtp_transport.cc +++ b/pc/rtp_transport.cc @@ -11,11 +11,13 @@ #include "pc/rtp_transport.h" #include -#include + +#include #include #include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/units/timestamp.h" #include "media/base/rtp_utils.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "rtc_base/checks.h" diff --git a/pc/rtp_transport.h b/pc/rtp_transport.h index 893d91e734..39d4ad5b54 100644 --- a/pc/rtp_transport.h +++ b/pc/rtp_transport.h @@ -18,6 +18,7 @@ #include "absl/types/optional.h" #include "call/rtp_demuxer.h" +#include "call/video_receive_stream.h" #include "modules/rtp_rtcp/include/rtp_header_extension_map.h" #include "p2p/base/packet_transport_internal.h" #include "pc/rtp_transport_internal.h" diff --git a/pc/rtp_transport_unittest.cc b/pc/rtp_transport_unittest.cc index aae6d2c462..0e6af734f3 100644 --- a/pc/rtp_transport_unittest.cc +++ b/pc/rtp_transport_unittest.cc @@ -10,16 +10,10 @@ #include "pc/rtp_transport.h" -#include -#include -#include -#include - -#include "api/rtp_headers.h" -#include "api/rtp_parameters.h" #include "p2p/base/fake_packet_transport.h" #include "pc/test/rtp_transport_test_util.h" #include "rtc_base/buffer.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" diff --git a/pc/scenario_tests/BUILD.gn b/pc/scenario_tests/BUILD.gn index bcb69b9129..fa3a67c9a2 100644 --- a/pc/scenario_tests/BUILD.gn +++ b/pc/scenario_tests/BUILD.gn @@ -16,7 +16,6 @@ if (rtc_include_tests) { "../../api:rtc_stats_api", "../../modules/rtp_rtcp:rtp_rtcp", "../../pc:pc_test_utils", - "../../pc:rtc_pc_base", "../../test:field_trial", "../../test:test_support", "../../test/peer_scenario:peer_scenario", diff --git a/pc/scenario_tests/goog_cc_test.cc b/pc/scenario_tests/goog_cc_test.cc index f0a30dfd86..ea96408ac7 100644 --- a/pc/scenario_tests/goog_cc_test.cc +++ b/pc/scenario_tests/goog_cc_test.cc @@ -75,7 +75,7 @@ TEST(GoogCcPeerScenarioTest, MAYBE_NoBweChangeFromVideoUnmute) { auto get_bwe = [&] { auto callback = rtc::make_ref_counted(); - caller->pc()->GetStats(callback); + caller->pc()->GetStats(callback.get()); s.net()->time_controller()->Wait([&] { return callback->called(); }); auto stats = callback->report()->GetStatsOfType()[0]; diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc index c63f820acf..e995a0e9a6 100644 --- a/pc/sctp_data_channel.cc +++ b/pc/sctp_data_channel.cc @@ -141,13 +141,13 @@ bool SctpSidAllocator::IsSidAvailable(int sid) const { } rtc::scoped_refptr SctpDataChannel::Create( - SctpDataChannelProviderInterface* provider, + SctpDataChannelControllerInterface* controller, const std::string& label, const InternalDataChannelInit& config, rtc::Thread* signaling_thread, rtc::Thread* network_thread) { auto channel = rtc::make_ref_counted( - config, provider, label, signaling_thread, network_thread); + config, controller, label, signaling_thread, network_thread); if (!channel->Init()) { return nullptr; } @@ -158,15 +158,12 @@ rtc::scoped_refptr SctpDataChannel::Create( rtc::scoped_refptr SctpDataChannel::CreateProxy( rtc::scoped_refptr channel) { // TODO(bugs.webrtc.org/11547): incorporate the network thread in the proxy. - // Also, consider allowing the proxy object to own the reference (std::move). - // As is, the proxy has a raw pointer and no reference to the channel object - // and trusting that the lifetime management aligns with the - // sctp_data_channels_ array in SctpDataChannelController. - return DataChannelProxy::Create(channel->signaling_thread_, channel.get()); + auto* signaling_thread = channel->signaling_thread_; + return DataChannelProxy::Create(signaling_thread, std::move(channel)); } SctpDataChannel::SctpDataChannel(const InternalDataChannelInit& config, - SctpDataChannelProviderInterface* provider, + SctpDataChannelControllerInterface* controller, const std::string& label, rtc::Thread* signaling_thread, rtc::Thread* network_thread) @@ -176,11 +173,16 @@ SctpDataChannel::SctpDataChannel(const InternalDataChannelInit& config, label_(label), config_(config), observer_(nullptr), - provider_(provider) { + controller_(controller) { RTC_DCHECK_RUN_ON(signaling_thread_); RTC_UNUSED(network_thread_); } +void SctpDataChannel::DetachFromController() { + RTC_DCHECK_RUN_ON(signaling_thread_); + controller_detached_ = true; +} + bool SctpDataChannel::Init() { RTC_DCHECK_RUN_ON(signaling_thread_); if (config_.id < -1 || @@ -217,7 +219,8 @@ bool SctpDataChannel::Init() { // This has to be done async because the upper layer objects (e.g. // Chrome glue and WebKit) are not wired up properly until after this // function returns. - if (provider_->ReadyToSendData()) { + RTC_DCHECK(!controller_detached_); + if (controller_->ReadyToSendData()) { AddRef(); rtc::Thread::Current()->PostTask(ToQueuedTask( [this] { @@ -258,7 +261,7 @@ uint64_t SctpDataChannel::buffered_amount() const { void SctpDataChannel::Close() { RTC_DCHECK_RUN_ON(signaling_thread_); - if (state_ == kClosed) + if (state_ == kClosing || state_ == kClosed) return; SetState(kClosing); // Will send queued data before beginning the underlying closing procedure. @@ -333,7 +336,8 @@ void SctpDataChannel::SetSctpSid(int sid) { } const_cast(config_).id = sid; - provider_->AddSctpDataStream(sid); + RTC_DCHECK(!controller_detached_); + controller_->AddSctpDataStream(sid); } void SctpDataChannel::OnClosingProcedureStartedRemotely(int sid) { @@ -359,20 +363,23 @@ void SctpDataChannel::OnClosingProcedureComplete(int sid) { // all pending data and transitioned to kClosing already. RTC_DCHECK_EQ(state_, kClosing); RTC_DCHECK(queued_send_data_.Empty()); - DisconnectFromProvider(); + DisconnectFromTransport(); SetState(kClosed); } } void SctpDataChannel::OnTransportChannelCreated() { RTC_DCHECK_RUN_ON(signaling_thread_); - if (!connected_to_provider_) { - connected_to_provider_ = provider_->ConnectDataChannel(this); + if (controller_detached_) { + return; + } + if (!connected_to_transport_) { + connected_to_transport_ = controller_->ConnectDataChannel(this); } - // The sid may have been unassigned when provider_->ConnectDataChannel was - // done. So always add the streams even if connected_to_provider_ is true. + // The sid may have been unassigned when controller_->ConnectDataChannel was + // done. So always add the streams even if connected_to_transport_ is true. if (config_.id >= 0) { - provider_->AddSctpDataStream(config_.id); + controller_->AddSctpDataStream(config_.id); } } @@ -475,8 +482,8 @@ void SctpDataChannel::CloseAbruptlyWithError(RTCError error) { return; } - if (connected_to_provider_) { - DisconnectFromProvider(); + if (connected_to_transport_) { + DisconnectFromTransport(); } // Closing abruptly means any queued data gets thrown away. @@ -506,7 +513,7 @@ void SctpDataChannel::UpdateState() { switch (state_) { case kConnecting: { - if (connected_to_provider_) { + if (connected_to_transport_) { if (handshake_state_ == kHandshakeShouldSendOpen) { rtc::CopyOnWriteBuffer payload; WriteDataChannelOpenMessage(label_, config_, &payload); @@ -537,10 +544,10 @@ void SctpDataChannel::UpdateState() { // to complete; after calling RemoveSctpDataStream, // OnClosingProcedureComplete will end up called asynchronously // afterwards. - if (connected_to_provider_ && !started_closing_procedure_ && - config_.id >= 0) { + if (connected_to_transport_ && !started_closing_procedure_ && + !controller_detached_ && config_.id >= 0) { started_closing_procedure_ = true; - provider_->RemoveSctpDataStream(config_.id); + controller_->RemoveSctpDataStream(config_.id); } } break; @@ -567,13 +574,13 @@ void SctpDataChannel::SetState(DataState state) { } } -void SctpDataChannel::DisconnectFromProvider() { +void SctpDataChannel::DisconnectFromTransport() { RTC_DCHECK_RUN_ON(signaling_thread_); - if (!connected_to_provider_) + if (!connected_to_transport_ || controller_detached_) return; - provider_->DisconnectDataChannel(this); - connected_to_provider_ = false; + controller_->DisconnectDataChannel(this); + connected_to_transport_ = false; } void SctpDataChannel::DeliverQueuedReceivedData() { @@ -612,6 +619,9 @@ bool SctpDataChannel::SendDataMessage(const DataBuffer& buffer, bool queue_if_blocked) { RTC_DCHECK_RUN_ON(signaling_thread_); SendDataParams send_params; + if (controller_detached_) { + return false; + } send_params.ordered = config_.ordered; // Send as ordered if it is still going through OPEN/ACK signaling. @@ -629,7 +639,7 @@ bool SctpDataChannel::SendDataMessage(const DataBuffer& buffer, cricket::SendDataResult send_result = cricket::SDR_SUCCESS; bool success = - provider_->SendData(config_.id, send_params, buffer.data, &send_result); + controller_->SendData(config_.id, send_params, buffer.data, &send_result); if (success) { ++messages_sent_; @@ -691,6 +701,9 @@ bool SctpDataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) { RTC_DCHECK(writable_); RTC_DCHECK_GE(config_.id, 0); + if (controller_detached_) { + return false; + } bool is_open_message = handshake_state_ == kHandshakeShouldSendOpen; RTC_DCHECK(!is_open_message || !config_.negotiated); @@ -703,7 +716,7 @@ bool SctpDataChannel::SendControlMessage(const rtc::CopyOnWriteBuffer& buffer) { cricket::SendDataResult send_result = cricket::SDR_SUCCESS; bool retval = - provider_->SendData(config_.id, send_params, buffer, &send_result); + controller_->SendData(config_.id, send_params, buffer, &send_result); if (retval) { RTC_LOG(LS_VERBOSE) << "Sent CONTROL message on channel " << config_.id; @@ -729,4 +742,10 @@ void SctpDataChannel::ResetInternalIdAllocatorForTesting(int new_value) { g_unique_id = new_value; } +SctpDataChannel* DowncastProxiedDataChannelInterfaceToSctpDataChannelForTesting( + DataChannelInterface* channel) { + return static_cast( + static_cast(channel)->internal()); +} + } // namespace webrtc diff --git a/pc/sctp_data_channel.h b/pc/sctp_data_channel.h index 56f99df3e5..a8442c59cc 100644 --- a/pc/sctp_data_channel.h +++ b/pc/sctp_data_channel.h @@ -37,7 +37,7 @@ class SctpDataChannel; // TODO(deadbeef): Get rid of this and have SctpDataChannel depend on // SctpTransportInternal (pure virtual SctpTransport interface) instead. -class SctpDataChannelProviderInterface { +class SctpDataChannelControllerInterface { public: // Sends the data to the transport. virtual bool SendData(int sid, @@ -57,7 +57,7 @@ class SctpDataChannelProviderInterface { virtual bool ReadyToSendData() const = 0; protected: - virtual ~SctpDataChannelProviderInterface() {} + virtual ~SctpDataChannelControllerInterface() {} }; // TODO(tommi): Change to not inherit from DataChannelInit but to have it as @@ -120,7 +120,7 @@ class SctpDataChannel : public DataChannelInterface, public sigslot::has_slots<> { public: static rtc::scoped_refptr Create( - SctpDataChannelProviderInterface* provider, + SctpDataChannelControllerInterface* controller, const std::string& label, const InternalDataChannelInit& config, rtc::Thread* signaling_thread, @@ -131,6 +131,9 @@ class SctpDataChannel : public DataChannelInterface, static rtc::scoped_refptr CreateProxy( rtc::scoped_refptr channel); + // Invalidate the link to the controller (DataChannelController); + void DetachFromController(); + void RegisterObserver(DataChannelObserver* observer) override; void UnregisterObserver() override; @@ -178,10 +181,10 @@ class SctpDataChannel : public DataChannelInterface, // Specializations of CloseAbruptlyWithError void CloseAbruptlyWithDataChannelFailure(const std::string& message); - // Slots for provider to connect signals to. + // Slots for controller to connect signals to. // // TODO(deadbeef): Make these private once we're hooking up signals ourselves, - // instead of relying on SctpDataChannelProviderInterface. + // instead of relying on SctpDataChannelControllerInterface. // Called when the SctpTransport's ready to use. That can happen when we've // finished negotiation, or if the channel was created after negotiation has @@ -223,7 +226,7 @@ class SctpDataChannel : public DataChannelInterface, protected: SctpDataChannel(const InternalDataChannelInit& config, - SctpDataChannelProviderInterface* client, + SctpDataChannelControllerInterface* client, const std::string& label, rtc::Thread* signaling_thread, rtc::Thread* network_thread); @@ -242,7 +245,7 @@ class SctpDataChannel : public DataChannelInterface, bool Init(); void UpdateState(); void SetState(DataState state); - void DisconnectFromProvider(); + void DisconnectFromTransport(); void DeliverQueuedReceivedData(); @@ -266,11 +269,12 @@ class SctpDataChannel : public DataChannelInterface, uint64_t bytes_sent_ RTC_GUARDED_BY(signaling_thread_) = 0; uint32_t messages_received_ RTC_GUARDED_BY(signaling_thread_) = 0; uint64_t bytes_received_ RTC_GUARDED_BY(signaling_thread_) = 0; - SctpDataChannelProviderInterface* const provider_ + SctpDataChannelControllerInterface* const controller_ RTC_GUARDED_BY(signaling_thread_); + bool controller_detached_ RTC_GUARDED_BY(signaling_thread_) = false; HandshakeState handshake_state_ RTC_GUARDED_BY(signaling_thread_) = kHandshakeInit; - bool connected_to_provider_ RTC_GUARDED_BY(signaling_thread_) = false; + bool connected_to_transport_ RTC_GUARDED_BY(signaling_thread_) = false; bool writable_ RTC_GUARDED_BY(signaling_thread_) = false; // Did we already start the graceful SCTP closing procedure? bool started_closing_procedure_ RTC_GUARDED_BY(signaling_thread_) = false; @@ -281,6 +285,11 @@ class SctpDataChannel : public DataChannelInterface, PacketQueue queued_send_data_ RTC_GUARDED_BY(signaling_thread_); }; +// Downcast a PeerConnectionInterface that points to a proxy object +// to its underlying SctpDataChannel object. For testing only. +SctpDataChannel* DowncastProxiedDataChannelInterfaceToSctpDataChannelForTesting( + DataChannelInterface* channel); + } // namespace webrtc #endif // PC_SCTP_DATA_CHANNEL_H_ diff --git a/pc/sctp_data_channel_transport.cc b/pc/sctp_data_channel_transport.cc index f01f86ebd8..626d1757b7 100644 --- a/pc/sctp_data_channel_transport.cc +++ b/pc/sctp_data_channel_transport.cc @@ -10,9 +10,6 @@ #include "pc/sctp_data_channel_transport.h" -#include "absl/types/optional.h" -#include "pc/sctp_utils.h" - namespace webrtc { SctpDataChannelTransport::SctpDataChannelTransport( diff --git a/pc/sctp_transport.h b/pc/sctp_transport.h index 16b98407b6..4981db4ede 100644 --- a/pc/sctp_transport.h +++ b/pc/sctp_transport.h @@ -16,9 +16,11 @@ #include "api/dtls_transport_interface.h" #include "api/scoped_refptr.h" #include "api/sctp_transport_interface.h" +#include "api/sequence_checker.h" #include "media/sctp/sctp_transport_internal.h" #include "p2p/base/dtls_transport_internal.h" #include "pc/dtls_transport.h" +#include "rtc_base/checks.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" diff --git a/pc/sctp_transport_unittest.cc b/pc/sctp_transport_unittest.cc index 679b481f4c..e3168d8b99 100644 --- a/pc/sctp_transport_unittest.cc +++ b/pc/sctp_transport_unittest.cc @@ -14,10 +14,17 @@ #include #include "absl/memory/memory.h" +#include "absl/types/optional.h" #include "api/dtls_transport_interface.h" +#include "api/transport/data_channel_transport_interface.h" +#include "media/base/media_channel.h" #include "p2p/base/fake_dtls_transport.h" +#include "p2p/base/p2p_constants.h" +#include "p2p/base/packet_transport_internal.h" #include "pc/dtls_transport.h" +#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/gunit.h" +#include "rtc_base/ref_counted_object.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/pc/sctp_utils.cc b/pc/sctp_utils.cc index f7458405ea..c60e339b08 100644 --- a/pc/sctp_utils.cc +++ b/pc/sctp_utils.cc @@ -11,12 +11,12 @@ #include "pc/sctp_utils.h" #include -#include + +#include #include "absl/types/optional.h" #include "api/priority.h" #include "rtc_base/byte_buffer.h" -#include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/logging.h" diff --git a/pc/sctp_utils_unittest.cc b/pc/sctp_utils_unittest.cc index af14fe4f6b..146886b8cb 100644 --- a/pc/sctp_utils_unittest.cc +++ b/pc/sctp_utils_unittest.cc @@ -12,6 +12,8 @@ #include +#include "absl/types/optional.h" +#include "api/priority.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/copy_on_write_buffer.h" #include "test/gtest.h" diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc index 2f4a88c68f..141d3fe79e 100644 --- a/pc/sdp_offer_answer.cc +++ b/pc/sdp_offer_answer.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "absl/algorithm/container.h" @@ -27,6 +26,7 @@ #include "api/array_view.h" #include "api/crypto/crypto_options.h" #include "api/dtls_transport_interface.h" +#include "api/field_trials_view.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" @@ -41,6 +41,7 @@ #include "p2p/base/transport_description_factory.h" #include "p2p/base/transport_info.h" #include "pc/channel_interface.h" +#include "pc/channel_manager.h" #include "pc/dtls_transport.h" #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" @@ -63,7 +64,6 @@ #include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" using cricket::ContentInfo; @@ -91,9 +91,6 @@ namespace { typedef webrtc::PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions; -constexpr const char* kAlwaysAllowPayloadTypeDemuxingFieldTrialName = - "WebRTC-AlwaysAllowPayloadTypeDemuxing"; - // Error messages const char kInvalidSdp[] = "Invalid session description."; const char kInvalidCandidates[] = "Description contains invalid candidates."; @@ -1102,10 +1099,10 @@ class SdpOfferAnswerHandler::SetSessionDescriptionObserverAdapter return; if (error.ok()) { handler_->pc_->message_handler()->PostSetSessionDescriptionSuccess( - inner_observer_); + inner_observer_.get()); } else { handler_->pc_->message_handler()->PostSetSessionDescriptionFailure( - inner_observer_, std::move(error)); + inner_observer_.get(), std::move(error)); } } @@ -1189,16 +1186,19 @@ std::unique_ptr SdpOfferAnswerHandler::Create( PeerConnectionDependencies& dependencies, ConnectionContext* context) { auto handler = absl::WrapUnique(new SdpOfferAnswerHandler(pc, context)); - handler->Initialize(configuration, dependencies); + handler->Initialize(configuration, dependencies, context); return handler; } void SdpOfferAnswerHandler::Initialize( const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies) { + PeerConnectionDependencies& dependencies, + ConnectionContext* context) { RTC_DCHECK_RUN_ON(signaling_thread()); + // 100 kbps is used by default, but can be overriden by a non-standard + // RTCConfiguration value (not available on Web). video_options_.screencast_min_bitrate_kbps = - configuration.screencast_min_bitrate; + configuration.screencast_min_bitrate.value_or(100); audio_options_.combined_audio_video_bwe = configuration.combined_audio_video_bwe; @@ -1225,13 +1225,13 @@ void SdpOfferAnswerHandler::Initialize( webrtc_session_desc_factory_ = std::make_unique( - signaling_thread(), channel_manager(), this, pc_->session_id(), - pc_->dtls_enabled(), std::move(dependencies.cert_generator), - certificate, + context, this, pc_->session_id(), pc_->dtls_enabled(), + std::move(dependencies.cert_generator), certificate, [this](const rtc::scoped_refptr& certificate) { RTC_DCHECK_RUN_ON(signaling_thread()); transport_controller_s()->SetLocalCertificate(certificate); - }); + }, + pc_->trials()); if (pc_->options()->disable_encryption) { webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); @@ -1946,6 +1946,13 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( const MediaContentDescription* media_desc = content->media_description(); RtpTransceiverDirection local_direction = RtpTransceiverDirectionReversed(media_desc->direction()); + // Remember the previous remote streams if this is a remote offer. This + // makes it possible to rollback modifications to the streams. + if (sdp_type == SdpType::kOffer) { + transceivers() + ->StableState(transceiver_ext) + ->SetRemoteStreamIds(transceiver->receiver()->stream_ids()); + } // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the // RTCSessionDescription: Set the associated remote streams given // transceiver.[[Receiver]], msids, addList, and removeList". @@ -1956,9 +1963,6 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( // The remote description has signaled the stream IDs. stream_ids = media_desc->streams()[0].stream_ids(); } - transceivers() - ->StableState(transceiver_ext) - ->SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name << " (" << GetStreamIdsString(stream_ids) << ")."; @@ -1988,6 +1992,14 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( &removed_streams); } // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. + if (sdp_type == SdpType::kOffer) { + // Remember the previous fired direction if this is a remote offer. This + // makes it possible to rollback modifications to [[FiredDirection]], + // which is necessary for "ontrack" to fire in or after rollback. + transceivers() + ->StableState(transceiver_ext) + ->SetFiredDirection(transceiver->fired_direction()); + } transceiver->set_fired_direction(local_direction); // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run // the following steps: @@ -2024,7 +2036,7 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( // Once all processing has finished, fire off callbacks. auto observer = pc_->Observer(); for (const auto& transceiver : now_receiving_transceivers) { - pc_->stats()->AddTrack(transceiver->receiver()->track()); + pc_->stats()->AddTrack(transceiver->receiver()->track().get()); observer->OnTrack(transceiver); observer->OnAddTrack(transceiver->receiver(), transceiver->receiver()->streams()); @@ -2071,7 +2083,7 @@ void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers( RtpTransceiverDirectionHasSend(audio_desc->direction()); UpdateRemoteSendersList(GetActiveStreams(audio_desc), default_audio_track_needed, audio_desc->type(), - new_streams); + new_streams.get()); } } @@ -2086,7 +2098,7 @@ void SdpOfferAnswerHandler::PlanBUpdateSendersAndReceivers( RtpTransceiverDirectionHasSend(video_desc->direction()); UpdateRemoteSendersList(GetActiveStreams(video_desc), default_video_track_needed, video_desc->type(), - new_streams); + new_streams.get()); } } @@ -2224,7 +2236,8 @@ void SdpOfferAnswerHandler::DoCreateOffer( std::string error = "CreateOffer called when PeerConnection is closed."; RTC_LOG(LS_ERROR) << error; pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + observer.get(), + RTCError(RTCErrorType::INVALID_STATE, std::move(error))); return; } @@ -2234,7 +2247,7 @@ void SdpOfferAnswerHandler::DoCreateOffer( std::string error_message = GetSessionErrorMsg(); RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message; pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, + observer.get(), RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); return; } @@ -2243,7 +2256,8 @@ void SdpOfferAnswerHandler::DoCreateOffer( std::string error = "CreateOffer called with invalid options."; RTC_LOG(LS_ERROR) << error; pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); + observer.get(), + RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); return; } @@ -2253,14 +2267,15 @@ void SdpOfferAnswerHandler::DoCreateOffer( RTCError error = HandleLegacyOfferOptions(options); if (!error.ok()) { pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, std::move(error)); + observer.get(), std::move(error)); return; } } cricket::MediaSessionOptions session_options; GetOptionsForOffer(options, &session_options); - webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); + webrtc_session_desc_factory_->CreateOffer(observer.get(), options, + session_options); } void SdpOfferAnswerHandler::CreateAnswer( @@ -2308,7 +2323,7 @@ void SdpOfferAnswerHandler::DoCreateAnswer( std::string error_message = GetSessionErrorMsg(); RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message; pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, + observer.get(), RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); return; } @@ -2320,7 +2335,8 @@ void SdpOfferAnswerHandler::DoCreateAnswer( "have-remote-offer or have-local-pranswer."; RTC_LOG(LS_ERROR) << error; pc_->message_handler()->PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); + observer.get(), + RTCError(RTCErrorType::INVALID_STATE, std::move(error))); return; } @@ -2344,7 +2360,7 @@ void SdpOfferAnswerHandler::DoCreateAnswer( cricket::MediaSessionOptions session_options; GetOptionsForAnswer(options, &session_options); - webrtc_session_desc_factory_->CreateAnswer(observer, session_options); + webrtc_session_desc_factory_->CreateAnswer(observer.get(), session_options); } void SdpOfferAnswerHandler::DoSetRemoteDescription( @@ -2770,7 +2786,7 @@ bool SdpOfferAnswerHandler::AddStream(MediaStreamInterface* local_stream) { if (pc_->IsClosed()) { return false; } - if (!CanAddLocalMediaStream(local_streams_, local_stream)) { + if (!CanAddLocalMediaStream(local_streams_.get(), local_stream)) { return false; } @@ -2889,6 +2905,8 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { } RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(IsUnifiedPlan()); + std::vector> + now_receiving_transceivers; std::vector> all_added_streams; std::vector> all_removed_streams; std::vector> removed_receivers; @@ -2897,6 +2915,22 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { auto transceiver = transceivers_stable_state_pair.first; auto state = transceivers_stable_state_pair.second; + if (state.did_set_fired_direction()) { + // If this rollback triggers going from not receiving to receving again, + // we need to fire "ontrack". + bool previously_fired_direction_is_recv = + transceiver->fired_direction().has_value() && + RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()); + bool currently_fired_direction_is_recv = + state.fired_direction().has_value() && + RtpTransceiverDirectionHasRecv(state.fired_direction().value()); + if (!previously_fired_direction_is_recv && + currently_fired_direction_is_recv) { + now_receiving_transceivers.push_back(transceiver); + } + transceiver->internal()->set_fired_direction(state.fired_direction()); + } + if (state.remote_stream_ids()) { std::vector> added_streams; std::vector> removed_streams; @@ -2913,8 +2947,12 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { } } + // Due to the above `continue` statement, the below code only runs if there + // is a change in mid association (has_m_section), if the transceiver was + // newly created (newly_created) or if remote streams were not set. + RTC_DCHECK(transceiver->internal()->mid().has_value()); - transceiver->internal()->SetChannel(nullptr, nullptr); + transceiver->internal()->ClearChannel(); if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && transceiver->receiver()) { @@ -2924,6 +2962,7 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { if (transceiver->internal()->reused_for_addtrack()) { transceiver->internal()->set_created_by_addtrack(true); } else { + transceiver->internal()->StopTransceiverProcedure(); transceivers()->Remove(transceiver); } } @@ -2946,6 +2985,11 @@ RTCError SdpOfferAnswerHandler::Rollback(SdpType desc_type) { ChangeSignalingState(PeerConnectionInterface::kStable); // Once all processing has finished, fire off callbacks. + for (const auto& transceiver : now_receiving_transceivers) { + pc_->Observer()->OnTrack(transceiver); + pc_->Observer()->OnAddTrack(transceiver->receiver(), + transceiver->receiver()->streams()); + } for (const auto& receiver : removed_receivers) { pc_->Observer()->OnRemoveTrack(receiver); } @@ -3386,9 +3430,9 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( AssociateTransceiver(source, new_session.GetType(), i, new_content, old_local_content, old_remote_content); if (!transceiver_or_error.ok()) { - // In the case where a transceiver is rejected locally, we don't - // expect to find a transceiver, but might find it in the case - // where state is still "stopping", not "stopped". + // In the case where a transceiver is rejected locally prior to being + // associated, we don't expect to find a transceiver, but might find it + // in the case where state is still "stopping", not "stopped". if (new_content.rejected) { continue; } @@ -3397,6 +3441,36 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiversAndDataChannels( auto transceiver = transceiver_or_error.MoveValue(); RTCError error = UpdateTransceiverChannel(transceiver, new_content, bundle_group); + // Handle locally rejected content. This code path is only needed for apps + // that SDP munge. Remote rejected content is handled in + // ApplyRemoteDescriptionUpdateTransceiverState(). + if (source == cricket::ContentSource::CS_LOCAL && new_content.rejected) { + // Local offer. + if (new_session.GetType() == SdpType::kOffer) { + // If the RtpTransceiver API was used, it would already have made the + // transceiver stopping. But if the rejection was caused by SDP + // munging then we need to ensure the transceiver is stopping here. + if (!transceiver->internal()->stopping()) { + transceiver->internal()->StopStandard(); + } + RTC_DCHECK(transceiver->internal()->stopping()); + } else { + // Local answer. + RTC_DCHECK(new_session.GetType() == SdpType::kAnswer || + new_session.GetType() == SdpType::kPrAnswer); + // When RtpTransceiver API is used, rejection happens in the offer and + // the transceiver will already be stopped at local answer time + // (calling stop between SRD(offer) and SLD(answer) would not reject + // the content in the answer - instead this would trigger a follow-up + // O/A exchange). So if the content was rejected but the transceiver + // is not already stopped, SDP munging has happened and we need to + // ensure the transceiver is stopped. + if (!transceiver->internal()->stopped()) { + transceiver->internal()->StopTransceiverProcedure(); + } + RTC_DCHECK(transceiver->internal()->stopped()); + } + } if (!error.ok()) { return error; } @@ -3568,26 +3642,21 @@ RTCError SdpOfferAnswerHandler::UpdateTransceiverChannel( cricket::ChannelInterface* channel = transceiver->internal()->channel(); if (content.rejected) { if (channel) { - transceiver->internal()->SetChannel(nullptr, nullptr); + transceiver->internal()->ClearChannel(); } } else { if (!channel) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - channel = CreateVoiceChannel(content.name); - } else { - RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type()); - channel = CreateVideoChannel(content.name); - } - if (!channel) { - return RTCError(RTCErrorType::INTERNAL_ERROR, - "Failed to create channel for mid=" + content.name); + auto error = transceiver->internal()->CreateChannel( + content.name, pc_->call_ptr(), pc_->configuration()->media_config, + pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(), + video_options(), video_bitrate_allocator_factory_.get(), + [&](absl::string_view mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); + if (!error.ok()) { + return error; } - // Note: this is a thread hop; the lambda will be executed - // on the network thread. - transceiver->internal()->SetChannel(channel, [&](const std::string& mid) { - RTC_DCHECK_RUN_ON(network_thread()); - return transport_controller_n()->GetRtpTransport(mid); - }); } } return RTCError::OK(); @@ -4240,7 +4309,7 @@ void SdpOfferAnswerHandler::RemoveRemoteStreamsIfEmpty( for (const auto& remote_stream : remote_streams) { if (remote_stream->GetAudioTracks().empty() && remote_stream->GetVideoTracks().empty()) { - remote_streams_->RemoveStream(remote_stream); + remote_streams_->RemoveStream(remote_stream.get()); removed_streams->push_back(remote_stream); } } @@ -4368,7 +4437,7 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList( rtp_manager()->FindSenderInfo(*current_senders, stream_id, sender_id); if (!sender_info) { current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); - rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream, + rtp_manager()->OnRemoteSenderAdded(current_senders->back(), stream.get(), media_type); } } @@ -4393,7 +4462,7 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList( current_senders->push_back( RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0)); rtp_manager()->OnRemoteSenderAdded(current_senders->back(), - default_stream, media_type); + default_stream.get(), media_type); } } } @@ -4566,14 +4635,12 @@ void SdpOfferAnswerHandler::RemoveUnusedChannels( // voice channel. const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); if (!video_info || video_info->rejected) { - rtp_manager()->GetVideoTransceiver()->internal()->SetChannel(nullptr, - nullptr); + rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel(); } const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); if (!audio_info || audio_info->rejected) { - rtp_manager()->GetAudioTransceiver()->internal()->SetChannel(nullptr, - nullptr); + rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel(); } const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); @@ -4628,7 +4695,7 @@ void SdpOfferAnswerHandler::UpdateEndedRemoteMediaStreams() { } for (auto& stream : streams_to_remove) { - remote_streams_->RemoveStream(stream); + remote_streams_->RemoveStream(stream.get()); pc_->Observer()->OnRemoveStream(std::move(stream)); } } @@ -4762,31 +4829,36 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc); if (voice && !voice->rejected && !rtp_manager()->GetAudioTransceiver()->internal()->channel()) { - cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); - if (!voice_channel) { - return RTCError(RTCErrorType::INTERNAL_ERROR, - "Failed to create voice channel."); + auto error = + rtp_manager()->GetAudioTransceiver()->internal()->CreateChannel( + voice->name, pc_->call_ptr(), pc_->configuration()->media_config, + pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options(), + video_options(), video_bitrate_allocator_factory_.get(), + [&](absl::string_view mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); + if (!error.ok()) { + return error; } - rtp_manager()->GetAudioTransceiver()->internal()->SetChannel( - voice_channel, [&](const std::string& mid) { - RTC_DCHECK_RUN_ON(network_thread()); - return transport_controller_n()->GetRtpTransport(mid); - }); } const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); if (video && !video->rejected && !rtp_manager()->GetVideoTransceiver()->internal()->channel()) { - cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); - if (!video_channel) { - return RTCError(RTCErrorType::INTERNAL_ERROR, - "Failed to create video channel."); + auto error = + rtp_manager()->GetVideoTransceiver()->internal()->CreateChannel( + video->name, pc_->call_ptr(), pc_->configuration()->media_config, + pc_->SrtpRequired(), pc_->GetCryptoOptions(), + + audio_options(), video_options(), + video_bitrate_allocator_factory_.get(), [&](absl::string_view mid) { + RTC_DCHECK_RUN_ON(network_thread()); + return transport_controller_n()->GetRtpTransport(mid); + }); + if (!error.ok()) { + return error; } - rtp_manager()->GetVideoTransceiver()->internal()->SetChannel( - video_channel, [&](const std::string& mid) { - RTC_DCHECK_RUN_ON(network_thread()); - return transport_controller_n()->GetRtpTransport(mid); - }); } const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc); @@ -4801,39 +4873,6 @@ RTCError SdpOfferAnswerHandler::CreateChannels(const SessionDescription& desc) { return RTCError::OK(); } -// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. -cricket::VoiceChannel* SdpOfferAnswerHandler::CreateVoiceChannel( - const std::string& mid) { - TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateVoiceChannel"); - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!channel_manager()->media_engine()) - return nullptr; - - // TODO(bugs.webrtc.org/11992): CreateVoiceChannel internally switches to the - // worker thread. We shouldn't be using the `call_ptr_` hack here but simply - // be on the worker thread and use `call_` (update upstream code). - return channel_manager()->CreateVoiceChannel( - pc_->call_ptr(), pc_->configuration()->media_config, mid, - pc_->SrtpRequired(), pc_->GetCryptoOptions(), audio_options()); -} - -// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. -cricket::VideoChannel* SdpOfferAnswerHandler::CreateVideoChannel( - const std::string& mid) { - TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::CreateVideoChannel"); - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!channel_manager()->media_engine()) - return nullptr; - - // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to the - // worker thread. We shouldn't be using the `call_ptr_` hack here but simply - // be on the worker thread and use `call_` (update upstream code). - return channel_manager()->CreateVideoChannel( - pc_->call_ptr(), pc_->configuration()->media_config, mid, - pc_->SrtpRequired(), pc_->GetCryptoOptions(), video_options(), - video_bitrate_allocator_factory_.get()); -} - bool SdpOfferAnswerHandler::CreateDataChannel(const std::string& mid) { RTC_DCHECK_RUN_ON(signaling_thread()); if (!context_->network_thread()->Invoke(RTC_FROM_HERE, [this, &mid] { @@ -4882,12 +4921,12 @@ void SdpOfferAnswerHandler::DestroyAllChannels() { for (const auto& transceiver : list) { if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - transceiver->internal()->SetChannel(nullptr, nullptr); + transceiver->internal()->ClearChannel(); } } for (const auto& transceiver : list) { if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - transceiver->internal()->SetChannel(nullptr, nullptr); + transceiver->internal()->ClearChannel(); } } @@ -5090,13 +5129,6 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( bool bundled_pt_demux_allowed_video = !IsUnifiedPlan() || mid_header_extension_missing_video || pt_demuxing_has_been_used_video_; - // Kill switch for the above change. - if (field_trial::IsEnabled(kAlwaysAllowPayloadTypeDemuxingFieldTrialName)) { - // TODO(https://crbug.com/webrtc/12814): If disabling PT-based demux does - // not trigger regressions, remove this kill switch. - bundled_pt_demux_allowed_audio = true; - bundled_pt_demux_allowed_video = true; - } // Gather all updates ahead of time so that all channels can be updated in a // single Invoke; necessary due to thread guards. diff --git a/pc/sdp_offer_answer.h b/pc/sdp_offer_answer.h index 622d57af24..de814ec737 100644 --- a/pc/sdp_offer_answer.h +++ b/pc/sdp_offer_answer.h @@ -15,12 +15,10 @@ #include #include -#include #include #include #include #include -#include #include #include "absl/types/optional.h" @@ -38,40 +36,27 @@ #include "api/sequence_checker.h" #include "api/set_local_description_observer_interface.h" #include "api/set_remote_description_observer_interface.h" -#include "api/transport/data_channel_transport_interface.h" -#include "api/turn_customizer.h" #include "api/uma_metrics.h" #include "api/video/video_bitrate_allocator_factory.h" #include "media/base/media_channel.h" #include "media/base/stream_params.h" #include "p2p/base/port_allocator.h" -#include "pc/channel.h" -#include "pc/channel_interface.h" -#include "pc/channel_manager.h" #include "pc/connection_context.h" #include "pc/data_channel_controller.h" -#include "pc/ice_server_parsing.h" #include "pc/jsep_transport_controller.h" #include "pc/media_session.h" #include "pc/media_stream_observer.h" #include "pc/peer_connection_internal.h" -#include "pc/rtc_stats_collector.h" #include "pc/rtp_receiver.h" -#include "pc/rtp_sender.h" #include "pc/rtp_transceiver.h" #include "pc/rtp_transmission_manager.h" -#include "pc/sctp_transport.h" #include "pc/sdp_state_provider.h" #include "pc/session_description.h" -#include "pc/stats_collector.h" #include "pc/stream_collection.h" #include "pc/transceiver_list.h" #include "pc/webrtc_session_description_factory.h" #include "rtc_base/checks.h" -#include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/operations_chain.h" -#include "rtc_base/race_checker.h" -#include "rtc_base/rtc_certificate.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" @@ -79,6 +64,10 @@ #include "rtc_base/unique_id_generator.h" #include "rtc_base/weak_ptr.h" +namespace cricket { +class ChannelManager; +} + namespace webrtc { // SdpOfferAnswerHandler is a component @@ -221,7 +210,8 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // once. Modifies dependencies. void Initialize( const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies& dependencies); + PeerConnectionDependencies& dependencies, + ConnectionContext* context); rtc::Thread* signaling_thread() const; rtc::Thread* network_thread() const; @@ -386,7 +376,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // to the SDP semantics. void FillInMissingRemoteMids(cricket::SessionDescription* remote_description); - // Returns an RtpTransciever, if available, that can be used to receive the + // Returns an RtpTransceiver, if available, that can be used to receive the // given media type according to JSEP rules. rtc::scoped_refptr> FindAvailableTransceiverToReceive(cricket::MediaType media_type) const; @@ -540,9 +530,6 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // This method will also delete any existing media channels before creating. RTCError CreateChannels(const cricket::SessionDescription& desc); - // Helper methods to create media channels. - cricket::VoiceChannel* CreateVoiceChannel(const std::string& mid); - cricket::VideoChannel* CreateVideoChannel(const std::string& mid); bool CreateDataChannel(const std::string& mid); // Destroys the RTP data channel transport and/or the SCTP data channel diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc index df343cce3a..2ce9e52d52 100644 --- a/pc/sdp_offer_answer_unittest.cc +++ b/pc/sdp_offer_answer_unittest.cc @@ -8,53 +8,29 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include - #include -#include #include #include -#include "absl/types/optional.h" #include "api/audio/audio_mixer.h" -#include "api/audio_codecs/audio_decoder_factory.h" -#include "api/audio_codecs/audio_encoder_factory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/create_peerconnection_factory.h" -#include "api/jsep.h" -#include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/peer_connection_interface.h" -#include "api/rtc_error.h" -#include "api/rtp_parameters.h" -#include "api/rtp_receiver_interface.h" -#include "api/rtp_sender_interface.h" #include "api/rtp_transceiver_interface.h" #include "api/scoped_refptr.h" -#include "api/set_remote_description_observer_interface.h" -#include "api/uma_metrics.h" #include "api/video_codecs/builtin_video_decoder_factory.h" #include "api/video_codecs/builtin_video_encoder_factory.h" -#include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_factory.h" -#include "media/base/stream_params.h" #include "modules/audio_device/include/audio_device.h" #include "modules/audio_processing/include/audio_processing.h" #include "p2p/base/port_allocator.h" -#include "pc/media_session.h" #include "pc/peer_connection_wrapper.h" -#include "pc/sdp_utils.h" -#include "pc/session_description.h" #include "pc/test/fake_audio_capture_module.h" #include "pc/test/mock_peer_connection_observers.h" -#include "rtc_base/checks.h" -#include "rtc_base/gunit.h" -#include "rtc_base/ref_counted_object.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/thread.h" #include "system_wrappers/include/metrics.h" -#include "test/gmock.h" #include "test/gtest.h" // This file contains unit tests that relate to the behavior of the @@ -106,12 +82,12 @@ class SdpOfferAnswerTest : public ::testing::Test { std::unique_ptr CreatePeerConnection( const RTCConfiguration& config) { auto observer = std::make_unique(); - auto pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr, - observer.get()); - EXPECT_TRUE(pc.get()); - observer->SetPeerConnectionInterface(pc.get()); - return std::make_unique(pc_factory_, pc, - std::move(observer)); + auto result = pc_factory_->CreatePeerConnectionOrError( + config, PeerConnectionDependencies(observer.get())); + EXPECT_TRUE(result.ok()); + observer->SetPeerConnectionInterface(result.value().get()); + return std::make_unique( + pc_factory_, result.MoveValue(), std::move(observer)); } protected: diff --git a/pc/sdp_serializer.cc b/pc/sdp_serializer.cc index cdd2f2e9ad..6d405d07a9 100644 --- a/pc/sdp_serializer.cc +++ b/pc/sdp_serializer.cc @@ -10,9 +10,9 @@ #include "pc/sdp_serializer.h" -#include #include #include +#include #include #include diff --git a/pc/sdp_serializer_unittest.cc b/pc/sdp_serializer_unittest.cc index 68d4c2acef..0c31750df4 100644 --- a/pc/sdp_serializer_unittest.cc +++ b/pc/sdp_serializer_unittest.cc @@ -10,12 +10,14 @@ #include "pc/sdp_serializer.h" +#include + #include #include #include #include -#include "rtc_base/gunit.h" +#include "test/gtest.h" using cricket::RidDescription; using cricket::RidDirection; diff --git a/pc/sdp_utils.cc b/pc/sdp_utils.cc index b750b04a46..ca61f0013f 100644 --- a/pc/sdp_utils.cc +++ b/pc/sdp_utils.cc @@ -10,8 +10,8 @@ #include "pc/sdp_utils.h" -#include #include +#include #include "api/jsep_session_description.h" #include "rtc_base/checks.h" diff --git a/pc/session_description.cc b/pc/session_description.cc index 7b878cbf7b..c1feedbf53 100644 --- a/pc/session_description.cc +++ b/pc/session_description.cc @@ -10,11 +10,10 @@ #include "pc/session_description.h" -#include - #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" namespace cricket { namespace { diff --git a/pc/session_description.h b/pc/session_description.h index ee7a91c84c..a68c312f42 100644 --- a/pc/session_description.h +++ b/pc/session_description.h @@ -15,9 +15,9 @@ #include #include -#include #include #include +#include #include #include diff --git a/pc/session_description_unittest.cc b/pc/session_description_unittest.cc index 00ce538398..4d0913bad2 100644 --- a/pc/session_description_unittest.cc +++ b/pc/session_description_unittest.cc @@ -9,8 +9,6 @@ */ #include "pc/session_description.h" -#include - #include "test/gtest.h" namespace cricket { diff --git a/pc/slow_peer_connection_integration_test.cc b/pc/slow_peer_connection_integration_test.cc new file mode 100644 index 0000000000..b45571ec22 --- /dev/null +++ b/pc/slow_peer_connection_integration_test.cc @@ -0,0 +1,512 @@ +/* + * Copyright 2022 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 is intended for PeerConnection integration tests that are +// slow to execute (currently defined as more than 5 seconds per test). + +#include + +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/dtmf_sender_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/units/time_delta.h" +#include "p2p/base/port_allocator.h" +#include "p2p/base/port_interface.h" +#include "p2p/base/stun_server.h" +#include "p2p/base/test_stun_server.h" +#include "pc/test/integration_test_helpers.h" +#include "pc/test/mock_peer_connection_observers.h" +#include "rtc_base/fake_clock.h" +#include "rtc_base/fake_network.h" +#include "rtc_base/firewall_socket_server.h" +#include "rtc_base/gunit.h" +#include "rtc_base/logging.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/test_certificate_verifier.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +class PeerConnectionIntegrationTest + : public PeerConnectionIntegrationBaseTest, + public ::testing::WithParamInterface< + std::tuple> { + protected: + PeerConnectionIntegrationTest() + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam()), + std::get<1>(GetParam())) {} +}; + +// Fake clock must be set before threads are started to prevent race on +// Set/GetClockForTesting(). +// To achieve that, multiple inheritance is used as a mixin pattern +// where order of construction is finely controlled. +// This also ensures peerconnection is closed before switching back to non-fake +// clock, avoiding other races and DCHECK failures such as in rtp_sender.cc. +class FakeClockForTest : public rtc::ScopedFakeClock { + protected: + FakeClockForTest() { + // Some things use a time of "0" as a special value, so we need to start out + // the fake clock at a nonzero time. + // TODO(deadbeef): Fix this. + AdvanceTime(webrtc::TimeDelta::Seconds(1)); + } + + // Explicit handle. + ScopedFakeClock& FakeClock() { return *this; } +}; + +// Ensure FakeClockForTest is constructed first (see class for rationale). +class PeerConnectionIntegrationTestWithFakeClock + : public FakeClockForTest, + public PeerConnectionIntegrationTest {}; + +class PeerConnectionIntegrationTestPlanB + : public PeerConnectionIntegrationBaseTest { + protected: + PeerConnectionIntegrationTestPlanB() + : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB_DEPRECATED) {} +}; + +class PeerConnectionIntegrationTestUnifiedPlan + : public PeerConnectionIntegrationBaseTest { + protected: + PeerConnectionIntegrationTestUnifiedPlan() + : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} +}; + +// Test the OnFirstPacketReceived callback from audio/video RtpReceivers. This +// includes testing that the callback is invoked if an observer is connected +// after the first packet has already been received. +TEST_P(PeerConnectionIntegrationTest, + RtpReceiverObserverOnFirstPacketReceived) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + // Start offer/answer exchange and wait for it to complete. + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Should be one receiver each for audio/video. + EXPECT_EQ(2U, caller()->rtp_receiver_observers().size()); + EXPECT_EQ(2U, callee()->rtp_receiver_observers().size()); + // Wait for all "first packet received" callbacks to be fired. + EXPECT_TRUE_WAIT( + absl::c_all_of(caller()->rtp_receiver_observers(), + [](const std::unique_ptr& o) { + return o->first_packet_received(); + }), + kMaxWaitForFramesMs); + EXPECT_TRUE_WAIT( + absl::c_all_of(callee()->rtp_receiver_observers(), + [](const std::unique_ptr& o) { + return o->first_packet_received(); + }), + kMaxWaitForFramesMs); + // If new observers are set after the first packet was already received, the + // callback should still be invoked. + caller()->ResetRtpReceiverObservers(); + callee()->ResetRtpReceiverObservers(); + EXPECT_EQ(2U, caller()->rtp_receiver_observers().size()); + EXPECT_EQ(2U, callee()->rtp_receiver_observers().size()); + EXPECT_TRUE( + absl::c_all_of(caller()->rtp_receiver_observers(), + [](const std::unique_ptr& o) { + return o->first_packet_received(); + })); + EXPECT_TRUE( + absl::c_all_of(callee()->rtp_receiver_observers(), + [](const std::unique_ptr& o) { + return o->first_packet_received(); + })); +} + +class DummyDtmfObserver : public DtmfSenderObserverInterface { + public: + DummyDtmfObserver() : completed_(false) {} + + // Implements DtmfSenderObserverInterface. + void OnToneChange(const std::string& tone) override { + tones_.push_back(tone); + if (tone.empty()) { + completed_ = true; + } + } + + const std::vector& tones() const { return tones_; } + bool completed() const { return completed_; } + + private: + bool completed_; + std::vector tones_; +}; + +TEST_P(PeerConnectionIntegrationTest, + SSLCertificateVerifierFailureUsedForTurnConnectionsFailsConnection) { + static const rtc::SocketAddress turn_server_internal_address{"88.88.88.0", + 3478}; + static const rtc::SocketAddress turn_server_external_address{"88.88.88.1", 0}; + + // Enable TCP-TLS for the fake turn server. We need to pass in 88.88.88.0 so + // that host name verification passes on the fake certificate. + CreateTurnServer(turn_server_internal_address, turn_server_external_address, + cricket::PROTO_TLS, "88.88.88.0"); + + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.urls.push_back("turns:88.88.88.0:3478?transport=tcp"); + ice_server.username = "test"; + ice_server.password = "test"; + + PeerConnectionInterface::RTCConfiguration client_1_config; + client_1_config.servers.push_back(ice_server); + client_1_config.type = webrtc::PeerConnectionInterface::kRelay; + + PeerConnectionInterface::RTCConfiguration client_2_config; + client_2_config.servers.push_back(ice_server); + // Setting the type to kRelay forces the connection to go through a TURN + // server. + client_2_config.type = webrtc::PeerConnectionInterface::kRelay; + + // Get a copy to the pointer so we can verify calls later. + rtc::TestCertificateVerifier* client_1_cert_verifier = + new rtc::TestCertificateVerifier(); + client_1_cert_verifier->verify_certificate_ = false; + rtc::TestCertificateVerifier* client_2_cert_verifier = + new rtc::TestCertificateVerifier(); + client_2_cert_verifier->verify_certificate_ = false; + + // Create the dependencies with the test certificate verifier. + webrtc::PeerConnectionDependencies client_1_deps(nullptr); + client_1_deps.tls_cert_verifier = + std::unique_ptr(client_1_cert_verifier); + webrtc::PeerConnectionDependencies client_2_deps(nullptr); + client_2_deps.tls_cert_verifier = + std::unique_ptr(client_2_cert_verifier); + + ASSERT_TRUE(CreatePeerConnectionWrappersWithConfigAndDeps( + client_1_config, std::move(client_1_deps), client_2_config, + std::move(client_2_deps))); + ConnectFakeSignaling(); + + // Set "offer to receive audio/video" without adding any tracks, so we just + // set up ICE/DTLS with no media. + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_audio = 1; + options.offer_to_receive_video = 1; + caller()->SetOfferAnswerOptions(options); + caller()->CreateAndSetAndSignalOffer(); + bool wait_res = true; + // TODO(bugs.webrtc.org/9219): When IceConnectionState is implemented + // properly, should be able to just wait for a state of "failed" instead of + // waiting a fixed 10 seconds. + WAIT_(DtlsConnected(), kDefaultTimeout, wait_res); + ASSERT_FALSE(wait_res); + + EXPECT_GT(client_1_cert_verifier->call_count_, 0u); + EXPECT_GT(client_2_cert_verifier->call_count_, 0u); +} + +// Test that we can get capture start ntp time. +TEST_P(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->AddAudioTrack(); + + callee()->AddAudioTrack(); + + // Do offer/answer, wait for the callee to receive some frames. + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + + // Get the remote audio track created on the receiver, so they can be used as + // GetStats filters. + auto receivers = callee()->pc()->GetReceivers(); + ASSERT_EQ(1u, receivers.size()); + auto remote_audio_track = receivers[0]->track(); + + // Get the audio output level stats. Note that the level is not available + // until an RTCP packet has been received. + EXPECT_TRUE_WAIT(callee()->OldGetStatsForTrack(remote_audio_track.get()) + ->CaptureStartNtpTime() > 0, + 2 * kMaxWaitForFramesMs); +} + +// Test that firewalling the ICE connection causes the clients to identify the +// disconnected state and then removing the firewall causes them to reconnect. +class PeerConnectionIntegrationIceStatesTest + : public PeerConnectionIntegrationBaseTest, + public ::testing::WithParamInterface< + std::tuple>> { + protected: + PeerConnectionIntegrationIceStatesTest() + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam())) { + port_allocator_flags_ = std::get<1>(std::get<1>(GetParam())); + } + + void StartStunServer(const SocketAddress& server_address) { + stun_server_.reset( + cricket::TestStunServer::Create(firewall(), server_address)); + } + + bool TestIPv6() { + return (port_allocator_flags_ & cricket::PORTALLOCATOR_ENABLE_IPV6); + } + + void SetPortAllocatorFlags() { + PeerConnectionIntegrationBaseTest::SetPortAllocatorFlags( + port_allocator_flags_, port_allocator_flags_); + } + + std::vector CallerAddresses() { + std::vector addresses; + addresses.push_back(SocketAddress("1.1.1.1", 0)); + if (TestIPv6()) { + addresses.push_back(SocketAddress("1111:0:a:b:c:d:e:f", 0)); + } + return addresses; + } + + std::vector CalleeAddresses() { + std::vector addresses; + addresses.push_back(SocketAddress("2.2.2.2", 0)); + if (TestIPv6()) { + addresses.push_back(SocketAddress("2222:0:a:b:c:d:e:f", 0)); + } + return addresses; + } + + void SetUpNetworkInterfaces() { + // Remove the default interfaces added by the test infrastructure. + caller()->network_manager()->RemoveInterface(kDefaultLocalAddress); + callee()->network_manager()->RemoveInterface(kDefaultLocalAddress); + + // Add network addresses for test. + for (const auto& caller_address : CallerAddresses()) { + caller()->network_manager()->AddInterface(caller_address); + } + for (const auto& callee_address : CalleeAddresses()) { + callee()->network_manager()->AddInterface(callee_address); + } + } + + private: + uint32_t port_allocator_flags_; + std::unique_ptr stun_server_; +}; + +// Ensure FakeClockForTest is constructed first (see class for rationale). +class PeerConnectionIntegrationIceStatesTestWithFakeClock + : public FakeClockForTest, + public PeerConnectionIntegrationIceStatesTest {}; + +#if !defined(THREAD_SANITIZER) +// This test provokes TSAN errors. bugs.webrtc.org/11282 + +// Tests that the PeerConnection goes through all the ICE gathering/connection +// states over the duration of the call. This includes Disconnected and Failed +// states, induced by putting a firewall between the peers and waiting for them +// to time out. +TEST_P(PeerConnectionIntegrationIceStatesTestWithFakeClock, VerifyIceStates) { + const SocketAddress kStunServerAddress = + SocketAddress("99.99.99.1", cricket::STUN_SERVER_PORT); + StartStunServer(kStunServerAddress); + + PeerConnectionInterface::RTCConfiguration config; + PeerConnectionInterface::IceServer ice_stun_server; + ice_stun_server.urls.push_back( + "stun:" + kStunServerAddress.HostAsURIString() + ":" + + kStunServerAddress.PortAsString()); + config.servers.push_back(ice_stun_server); + + ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config)); + ConnectFakeSignaling(); + SetPortAllocatorFlags(); + SetUpNetworkInterfaces(); + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + + // Initial state before anything happens. + ASSERT_EQ(PeerConnectionInterface::kIceGatheringNew, + caller()->ice_gathering_state()); + ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, + caller()->ice_connection_state()); + ASSERT_EQ(PeerConnectionInterface::kIceConnectionNew, + caller()->standardized_ice_connection_state()); + + // Start the call by creating the offer, setting it as the local description, + // then sending it to the peer who will respond with an answer. This happens + // asynchronously so that we can watch the states as it runs in the + // background. + caller()->CreateAndSetAndSignalOffer(); + + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, + caller()->ice_connection_state(), kDefaultTimeout, + FakeClock()); + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, + caller()->standardized_ice_connection_state(), + kDefaultTimeout, FakeClock()); + + // Verify that the observer was notified of the intermediate transitions. + EXPECT_THAT(caller()->ice_connection_state_history(), + ElementsAre(PeerConnectionInterface::kIceConnectionChecking, + PeerConnectionInterface::kIceConnectionConnected, + PeerConnectionInterface::kIceConnectionCompleted)); + EXPECT_THAT(caller()->standardized_ice_connection_state_history(), + ElementsAre(PeerConnectionInterface::kIceConnectionChecking, + PeerConnectionInterface::kIceConnectionConnected, + PeerConnectionInterface::kIceConnectionCompleted)); + EXPECT_THAT( + caller()->peer_connection_state_history(), + ElementsAre(PeerConnectionInterface::PeerConnectionState::kConnecting, + PeerConnectionInterface::PeerConnectionState::kConnected)); + EXPECT_THAT(caller()->ice_gathering_state_history(), + ElementsAre(PeerConnectionInterface::kIceGatheringGathering, + PeerConnectionInterface::kIceGatheringComplete)); + + // Block connections to/from the caller and wait for ICE to become + // disconnected. + for (const auto& caller_address : CallerAddresses()) { + firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); + } + RTC_LOG(LS_INFO) << "Firewall rules applied"; + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, + caller()->ice_connection_state(), kDefaultTimeout, + FakeClock()); + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionDisconnected, + caller()->standardized_ice_connection_state(), + kDefaultTimeout, FakeClock()); + + // Let ICE re-establish by removing the firewall rules. + firewall()->ClearRules(); + RTC_LOG(LS_INFO) << "Firewall rules cleared"; + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, + caller()->ice_connection_state(), kDefaultTimeout, + FakeClock()); + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionCompleted, + caller()->standardized_ice_connection_state(), + kDefaultTimeout, FakeClock()); + + // According to RFC7675, if there is no response within 30 seconds then the + // peer should consider the other side to have rejected the connection. This + // is signaled by the state transitioning to "failed". + constexpr int kConsentTimeout = 30000; + for (const auto& caller_address : CallerAddresses()) { + firewall()->AddRule(false, rtc::FP_ANY, rtc::FD_ANY, caller_address); + } + RTC_LOG(LS_INFO) << "Firewall rules applied again"; + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, + caller()->ice_connection_state(), kConsentTimeout, + FakeClock()); + ASSERT_EQ_SIMULATED_WAIT(PeerConnectionInterface::kIceConnectionFailed, + caller()->standardized_ice_connection_state(), + kConsentTimeout, FakeClock()); +} +#endif + +// This test sets up a call that's transferred to a new caller with a different +// DTLS fingerprint. +TEST_P(PeerConnectionIntegrationTest, CallTransferredForCallee) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + + // Keep the original peer around which will still send packets to the + // receiving client. These SRTP packets will be dropped. + std::unique_ptr original_peer( + SetCallerPcWrapperAndReturnCurrent( + CreatePeerConnectionWrapperWithAlternateKey().release())); + // TODO(deadbeef): Why do we call Close here? That goes against the comment + // directly above. + original_peer->pc()->Close(); + + ConnectFakeSignaling(); + caller()->AddAudioVideoTracks(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Wait for some additional frames to be transmitted end-to-end. + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); +} + +// This test sets up a call that's transferred to a new callee with a different +// DTLS fingerprint. +TEST_P(PeerConnectionIntegrationTest, CallTransferredForCaller) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + + // Keep the original peer around which will still send packets to the + // receiving client. These SRTP packets will be dropped. + std::unique_ptr original_peer( + SetCalleePcWrapperAndReturnCurrent( + CreatePeerConnectionWrapperWithAlternateKey().release())); + // TODO(deadbeef): Why do we call Close here? That goes against the comment + // directly above. + original_peer->pc()->Close(); + + ConnectFakeSignaling(); + callee()->AddAudioVideoTracks(); + caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions()); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Wait for some additional frames to be transmitted end-to-end. + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); +} + +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationTest, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), + Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", + "WebRTC-FrameBuffer3/arm:SyncDecoding/"))); + +constexpr uint32_t kFlagsIPv4NoStun = cricket::PORTALLOCATOR_DISABLE_TCP | + cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_DISABLE_RELAY; +constexpr uint32_t kFlagsIPv6NoStun = + cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_STUN | + cricket::PORTALLOCATOR_ENABLE_IPV6 | cricket::PORTALLOCATOR_DISABLE_RELAY; +constexpr uint32_t kFlagsIPv4Stun = + cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_RELAY; + +INSTANTIATE_TEST_SUITE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationIceStatesTestWithFakeClock, + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, SdpSemantics::kUnifiedPlan), + Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), + std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), + std::make_pair("IPv4 with STUN", kFlagsIPv4Stun)))); + +} // namespace +} // namespace webrtc diff --git a/pc/srtp_filter.cc b/pc/srtp_filter.cc index c48dfdb4cd..9d7f39a7a3 100644 --- a/pc/srtp_filter.cc +++ b/pc/srtp_filter.cc @@ -11,8 +11,8 @@ #include "pc/srtp_filter.h" #include -#include -#include + +#include #include "absl/strings/match.h" #include "rtc_base/logging.h" diff --git a/pc/srtp_session.cc b/pc/srtp_session.cc index 76ab3a8fe8..7d1aaf2d65 100644 --- a/pc/srtp_session.cc +++ b/pc/srtp_session.cc @@ -10,23 +10,106 @@ #include "pc/srtp_session.h" +#include + #include +#include #include "absl/base/attributes.h" +#include "absl/base/const_init.h" +#include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/field_trials_view.h" #include "modules/rtp_rtcp/source/rtp_util.h" #include "pc/external_hmac.h" +#include "rtc_base/byte_order.h" +#include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/time_utils.h" -#include "system_wrappers/include/field_trial.h" #include "system_wrappers/include/metrics.h" #include "third_party/libsrtp/include/srtp.h" #include "third_party/libsrtp/include/srtp_priv.h" namespace cricket { +namespace { +class LibSrtpInitializer { + public: + // Returns singleton instance of this class. Instance created on first use, + // and never destroyed. + static LibSrtpInitializer& Get() { + static LibSrtpInitializer* const instance = new LibSrtpInitializer(); + return *instance; + } + void ProhibitLibsrtpInitialization(); + + // These methods are responsible for initializing libsrtp (if the usage count + // is incremented from 0 to 1) or deinitializing it (when decremented from 1 + // to 0). + // + // Returns true if successful (will always be successful if already inited). + bool IncrementLibsrtpUsageCountAndMaybeInit( + srtp_event_handler_func_t* handler); + void DecrementLibsrtpUsageCountAndMaybeDeinit(); + + private: + LibSrtpInitializer() = default; + + webrtc::Mutex mutex_; + int usage_count_ RTC_GUARDED_BY(mutex_) = 0; +}; + +void LibSrtpInitializer::ProhibitLibsrtpInitialization() { + webrtc::MutexLock lock(&mutex_); + ++usage_count_; +} + +bool LibSrtpInitializer::IncrementLibsrtpUsageCountAndMaybeInit( + srtp_event_handler_func_t* handler) { + webrtc::MutexLock lock(&mutex_); + + RTC_DCHECK_GE(usage_count_, 0); + if (usage_count_ == 0) { + int err; + err = srtp_init(); + if (err != srtp_err_status_ok) { + RTC_LOG(LS_ERROR) << "Failed to init SRTP, err=" << err; + return false; + } + + err = srtp_install_event_handler(handler); + if (err != srtp_err_status_ok) { + RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; + return false; + } + + err = external_crypto_init(); + if (err != srtp_err_status_ok) { + RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err; + return false; + } + } + ++usage_count_; + return true; +} + +void LibSrtpInitializer::DecrementLibsrtpUsageCountAndMaybeDeinit() { + webrtc::MutexLock lock(&mutex_); + + RTC_DCHECK_GE(usage_count_, 1); + if (--usage_count_ == 0) { + int err = srtp_shutdown(); + if (err) { + RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err; + } + } +} + +} // namespace + using ::webrtc::ParseRtpSequenceNumber; // One more than the maximum libsrtp error code. Required by @@ -34,8 +117,10 @@ using ::webrtc::ParseRtpSequenceNumber; // in srtp.h. constexpr int kSrtpErrorCodeBoundary = 28; -SrtpSession::SrtpSession() { - dump_plain_rtp_ = webrtc::field_trial::IsEnabled("WebRTC-Debugging-RtpDump"); +SrtpSession::SrtpSession() {} + +SrtpSession::SrtpSession(const webrtc::FieldTrialsView& field_trials) { + dump_plain_rtp_ = field_trials.IsEnabled("WebRTC-Debugging-RtpDump"); } SrtpSession::~SrtpSession() { @@ -44,7 +129,7 @@ SrtpSession::~SrtpSession() { srtp_dealloc(session_); } if (inited_) { - DecrementLibsrtpUsageCountAndMaybeDeinit(); + LibSrtpInitializer::Get().DecrementLibsrtpUsageCountAndMaybeDeinit(); } } @@ -345,7 +430,8 @@ bool SrtpSession::SetKey(int type, // This is the first time we need to actually interact with libsrtp, so // initialize it if needed. - if (IncrementLibsrtpUsageCountAndMaybeInit()) { + if (LibSrtpInitializer::Get().IncrementLibsrtpUsageCountAndMaybeInit( + &SrtpSession::HandleEventThunk)) { inited_ = true; } else { return false; @@ -368,54 +454,8 @@ bool SrtpSession::UpdateKey(int type, return DoSetKey(type, cs, key, len, extension_ids); } -ABSL_CONST_INIT int g_libsrtp_usage_count = 0; -ABSL_CONST_INIT webrtc::GlobalMutex g_libsrtp_lock(absl::kConstInit); - void ProhibitLibsrtpInitialization() { - webrtc::GlobalMutexLock ls(&g_libsrtp_lock); - ++g_libsrtp_usage_count; -} - -// static -bool SrtpSession::IncrementLibsrtpUsageCountAndMaybeInit() { - webrtc::GlobalMutexLock ls(&g_libsrtp_lock); - - RTC_DCHECK_GE(g_libsrtp_usage_count, 0); - if (g_libsrtp_usage_count == 0) { - int err; - err = srtp_init(); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_ERROR) << "Failed to init SRTP, err=" << err; - return false; - } - - err = srtp_install_event_handler(&SrtpSession::HandleEventThunk); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_ERROR) << "Failed to install SRTP event handler, err=" << err; - return false; - } - - err = external_crypto_init(); - if (err != srtp_err_status_ok) { - RTC_LOG(LS_ERROR) << "Failed to initialize fake auth, err=" << err; - return false; - } - } - ++g_libsrtp_usage_count; - return true; -} - -// static -void SrtpSession::DecrementLibsrtpUsageCountAndMaybeDeinit() { - webrtc::GlobalMutexLock ls(&g_libsrtp_lock); - - RTC_DCHECK_GE(g_libsrtp_usage_count, 1); - if (--g_libsrtp_usage_count == 0) { - int err = srtp_shutdown(); - if (err) { - RTC_LOG(LS_ERROR) << "srtp_shutdown failed. err=" << err; - } - } + LibSrtpInitializer::Get().ProhibitLibsrtpInitialization(); } void SrtpSession::HandleEvent(const srtp_event_data_t* ev) { @@ -463,13 +503,16 @@ void SrtpSession::DumpPacket(const void* buf, int len, bool outbound) { int64_t minutes = (time_of_day / (60 * 1000)) % 60; int64_t seconds = (time_of_day / 1000) % 60; int64_t millis = time_of_day % 1000; - RTC_LOG(LS_VERBOSE) << "\n" << (outbound ? "O" : "I") << " " - << std::setfill('0') << std::setw(2) << hours << ":" - << std::setfill('0') << std::setw(2) << minutes << ":" - << std::setfill('0') << std::setw(2) << seconds << "." - << std::setfill('0') << std::setw(3) << millis << " " - << "000000 " << rtc::hex_encode_with_delimiter((const char *)buf, len, ' ') - << " # RTP_DUMP"; + RTC_LOG(LS_VERBOSE) << "\n" + << (outbound ? "O" : "I") << " " << std::setfill('0') + << std::setw(2) << hours << ":" << std::setfill('0') + << std::setw(2) << minutes << ":" << std::setfill('0') + << std::setw(2) << seconds << "." << std::setfill('0') + << std::setw(3) << millis << " " + << "000000 " + << rtc::hex_encode_with_delimiter( + absl::string_view((const char*)buf, len), ' ') + << " # RTP_DUMP"; } } // namespace cricket diff --git a/pc/srtp_session.h b/pc/srtp_session.h index f1b6a52b47..048e665644 100644 --- a/pc/srtp_session.h +++ b/pc/srtp_session.h @@ -11,8 +11,12 @@ #ifndef PC_SRTP_SESSION_H_ #define PC_SRTP_SESSION_H_ +#include +#include + #include +#include "api/field_trials_view.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" #include "rtc_base/synchronization/mutex.h" @@ -32,6 +36,7 @@ void ProhibitLibsrtpInitialization(); class SrtpSession { public: SrtpSession(); + explicit SrtpSession(const webrtc::FieldTrialsView& field_trials); ~SrtpSession(); SrtpSession(const SrtpSession&) = delete; @@ -115,14 +120,6 @@ class SrtpSession { // for debugging. void DumpPacket(const void* buf, int len, bool outbound); - // These methods are responsible for initializing libsrtp (if the usage count - // is incremented from 0 to 1) or deinitializing it (when decremented from 1 - // to 0). - // - // Returns true if successful (will always be successful if already inited). - static bool IncrementLibsrtpUsageCountAndMaybeInit(); - static void DecrementLibsrtpUsageCountAndMaybeDeinit(); - void HandleEvent(const srtp_event_data_t* ev); static void HandleEventThunk(srtp_event_data_t* ev); @@ -137,7 +134,6 @@ class SrtpSession { int rtcp_auth_tag_len_ = 0; bool inited_ = false; - static webrtc::GlobalMutex lock_; int last_send_seq_num_ = -1; bool external_auth_active_ = false; bool external_auth_enabled_ = false; diff --git a/pc/srtp_session_unittest.cc b/pc/srtp_session_unittest.cc index dc08c2e908..16a840a307 100644 --- a/pc/srtp_session_unittest.cc +++ b/pc/srtp_session_unittest.cc @@ -21,6 +21,7 @@ #include "system_wrappers/include/metrics.h" #include "test/gmock.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" #include "third_party/libsrtp/include/srtp.h" using ::testing::ElementsAre; @@ -32,7 +33,9 @@ std::vector kEncryptedHeaderExtensionIds; class SrtpSessionTest : public ::testing::Test { public: - SrtpSessionTest() { webrtc::metrics::Reset(); } + SrtpSessionTest() : s1_(field_trials_), s2_(field_trials_) { + webrtc::metrics::Reset(); + } protected: virtual void SetUp() { @@ -69,6 +72,7 @@ class SrtpSessionTest : public ::testing::Test { EXPECT_EQ(expected_len, out_len); EXPECT_EQ(0, memcmp(rtcp_packet_, kRtcpReport, out_len)); } + webrtc::test::ScopedKeyValueConfig field_trials_; cricket::SrtpSession s1_; cricket::SrtpSession s2_; char rtp_packet_[sizeof(kPcmuFrame) + 10]; diff --git a/pc/srtp_transport.cc b/pc/srtp_transport.cc index 230c1a347b..0991a25fb5 100644 --- a/pc/srtp_transport.cc +++ b/pc/srtp_transport.cc @@ -34,8 +34,9 @@ namespace webrtc { -SrtpTransport::SrtpTransport(bool rtcp_mux_enabled) - : RtpTransport(rtcp_mux_enabled) {} +SrtpTransport::SrtpTransport(bool rtcp_mux_enabled, + const FieldTrialsView& field_trials) + : RtpTransport(rtcp_mux_enabled), field_trials_(field_trials) {} RTCError SrtpTransport::SetSrtpSendKey(const cricket::CryptoParams& params) { if (send_params_) { @@ -324,13 +325,13 @@ bool SrtpTransport::SetRtcpParams(int send_cs, return false; } - send_rtcp_session_.reset(new cricket::SrtpSession()); + send_rtcp_session_.reset(new cricket::SrtpSession(field_trials_)); if (!send_rtcp_session_->SetSend(send_cs, send_key, send_key_len, send_extension_ids)) { return false; } - recv_rtcp_session_.reset(new cricket::SrtpSession()); + recv_rtcp_session_.reset(new cricket::SrtpSession(field_trials_)); if (!recv_rtcp_session_->SetRecv(recv_cs, recv_key, recv_key_len, recv_extension_ids)) { return false; @@ -361,8 +362,8 @@ void SrtpTransport::ResetParams() { } void SrtpTransport::CreateSrtpSessions() { - send_session_.reset(new cricket::SrtpSession()); - recv_session_.reset(new cricket::SrtpSession()); + send_session_.reset(new cricket::SrtpSession(field_trials_)); + recv_session_.reset(new cricket::SrtpSession(field_trials_)); if (external_auth_enabled_) { send_session_->EnableExternalAuth(); } diff --git a/pc/srtp_transport.h b/pc/srtp_transport.h index 4bc028d68e..e4b0d9fff6 100644 --- a/pc/srtp_transport.h +++ b/pc/srtp_transport.h @@ -20,6 +20,7 @@ #include "absl/types/optional.h" #include "api/crypto_params.h" +#include "api/field_trials_view.h" #include "api/rtc_error.h" #include "p2p/base/packet_transport_internal.h" #include "pc/rtp_transport.h" @@ -36,7 +37,7 @@ namespace webrtc { // parameters for the SrtpSession underneath. class SrtpTransport : public RtpTransport { public: - explicit SrtpTransport(bool rtcp_mux_enabled); + SrtpTransport(bool rtcp_mux_enabled, const FieldTrialsView& field_trials); virtual ~SrtpTransport() = default; @@ -167,6 +168,8 @@ class SrtpTransport : public RtpTransport { int rtp_abs_sendtime_extn_id_ = -1; int decryption_failure_count_ = 0; + + const FieldTrialsView& field_trials_; }; } // namespace webrtc diff --git a/pc/srtp_transport_unittest.cc b/pc/srtp_transport_unittest.cc index 59bc8e8090..4e643935a9 100644 --- a/pc/srtp_transport_unittest.cc +++ b/pc/srtp_transport_unittest.cc @@ -12,8 +12,6 @@ #include -#include -#include #include #include "call/rtp_demuxer.h" @@ -25,9 +23,11 @@ #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" using rtc::kSrtpAeadAes128Gcm; using rtc::kTestKey1; @@ -58,8 +58,10 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { rtp_packet_transport1_->SetDestination(rtp_packet_transport2_.get(), asymmetric); - srtp_transport1_ = std::make_unique(rtcp_mux_enabled); - srtp_transport2_ = std::make_unique(rtcp_mux_enabled); + srtp_transport1_ = + std::make_unique(rtcp_mux_enabled, field_trials_); + srtp_transport2_ = + std::make_unique(rtcp_mux_enabled, field_trials_); srtp_transport1_->SetRtpPacketTransport(rtp_packet_transport1_.get()); srtp_transport2_->SetRtpPacketTransport(rtp_packet_transport2_.get()); @@ -334,6 +336,7 @@ class SrtpTransportTest : public ::testing::Test, public sigslot::has_slots<> { TransportObserver rtp_sink2_; int sequence_number_ = 0; + webrtc::test::ScopedKeyValueConfig field_trials_; }; class SrtpTransportTestWithExternalAuth diff --git a/pc/stats_collector.cc b/pc/stats_collector.cc index 493c26cfbd..97dd5bdba2 100644 --- a/pc/stats_collector.cc +++ b/pc/stats_collector.cc @@ -13,18 +13,20 @@ #include #include +#include #include -#include +#include #include #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/audio_codecs/audio_encoder.h" #include "api/candidate.h" #include "api/data_channel_interface.h" +#include "api/field_trials_view.h" #include "api/media_types.h" -#include "api/rtp_receiver_interface.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -35,10 +37,11 @@ #include "modules/audio_processing/include/audio_processing_statistics.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" -#include "pc/channel.h" #include "pc/channel_interface.h" #include "pc/data_channel_utils.h" #include "pc/rtp_receiver.h" +#include "pc/rtp_receiver_proxy.h" +#include "pc/rtp_sender_proxy.h" #include "pc/rtp_transceiver.h" #include "pc/transport_stats.h" #include "rtc_base/checks.h" @@ -52,7 +55,6 @@ #include "rtc_base/thread.h" #include "rtc_base/time_utils.h" #include "rtc_base/trace_event.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -140,10 +142,7 @@ void ExtractCommonReceiveProperties(const cricket::MediaReceiverInfo& info, } void SetAudioProcessingStats(StatsReport* report, - bool typing_noise_detected, const AudioProcessingStats& apm_stats) { - report->AddBoolean(StatsReport::kStatsValueNameTypingNoiseState, - typing_noise_detected); if (apm_stats.delay_median_ms) { report->AddInt(StatsReport::kStatsValueNameEchoDelayMedian, *apm_stats.delay_median_ms); @@ -242,8 +241,7 @@ void ExtractStats(const cricket::VoiceSenderInfo& info, bool use_standard_bytes_stats) { ExtractCommonSendProperties(info, report, use_standard_bytes_stats); - SetAudioProcessingStats(report, info.typing_noise_detected, - info.apm_statistics); + SetAudioProcessingStats(report, info.apm_statistics); const FloatForAdd floats[] = { {StatsReport::kStatsValueNameTotalAudioEnergy, info.total_input_energy}, @@ -540,7 +538,7 @@ StatsCollector::StatsCollector(PeerConnectionInternal* pc) : pc_(pc), stats_gathering_started_(0), use_standard_bytes_stats_( - webrtc::field_trial::IsEnabled(kUseStandardBytesStats)) { + pc->trials().IsEnabled(kUseStandardBytesStats)) { RTC_DCHECK(pc_); } @@ -1036,21 +1034,21 @@ void StatsCollector::ExtractBweInfo() { // Fill in target encoder bitrate, actual encoder bitrate, rtx bitrate, etc. // TODO(holmer): Also fill this in for audio. auto transceivers = pc_->GetTransceiversInternal(); - std::vector video_channels; + std::vector video_media_channels; for (const auto& transceiver : transceivers) { if (transceiver->media_type() != cricket::MEDIA_TYPE_VIDEO) { continue; } - auto* video_channel = - static_cast(transceiver->internal()->channel()); + auto* video_channel = transceiver->internal()->channel(); if (video_channel) { - video_channels.push_back(video_channel); + video_media_channels.push_back(static_cast( + video_channel->media_channel())); } } - if (!video_channels.empty()) { + if (!video_media_channels.empty()) { pc_->worker_thread()->Invoke(RTC_FROM_HERE, [&] { - for (const auto& channel : video_channels) { + for (const auto& channel : video_media_channels) { channel->FillBitrateInfo(&bwe_info); } }); @@ -1351,8 +1349,7 @@ void StatsCollector::UpdateReportFromAudioTrack(AudioTrackInterface* track, AudioProcessorInterface::AudioProcessorStatistics stats = audio_processor->GetStats(has_remote_tracks); - SetAudioProcessingStats(report, stats.typing_noise_detected, - stats.apm_statistics); + SetAudioProcessingStats(report, stats.apm_statistics); } } diff --git a/pc/stats_collector.h b/pc/stats_collector.h index 71b802dd09..ea719c69a5 100644 --- a/pc/stats_collector.h +++ b/pc/stats_collector.h @@ -21,18 +21,25 @@ #include #include #include +#include #include #include +#include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/media_stream_interface.h" #include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" #include "api/stats_types.h" #include "p2p/base/connection_info.h" #include "p2p/base/port.h" #include "pc/peer_connection_internal.h" +#include "pc/rtp_transceiver.h" #include "pc/stats_collector_interface.h" +#include "pc/transport_stats.h" #include "rtc_base/network_constants.h" #include "rtc_base/ssl_certificate.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { diff --git a/pc/stats_collector_unittest.cc b/pc/stats_collector_unittest.cc index c9740460db..5af8be771a 100644 --- a/pc/stats_collector_unittest.cc +++ b/pc/stats_collector_unittest.cc @@ -12,7 +12,7 @@ #include -#include +#include #include "absl/algorithm/container.h" #include "absl/types/optional.h" @@ -20,11 +20,16 @@ #include "api/candidate.h" #include "api/data_channel_interface.h" #include "api/media_stream_track.h" +#include "api/media_types.h" +#include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "call/call.h" #include "media/base/media_channel.h" #include "modules/audio_processing/include/audio_processing_statistics.h" +#include "p2p/base/ice_transport_internal.h" #include "pc/media_stream.h" +#include "pc/rtp_receiver.h" +#include "pc/rtp_sender.h" #include "pc/sctp_data_channel.h" #include "pc/test/fake_peer_connection_for_stats.h" #include "pc/test/fake_video_track_source.h" @@ -43,6 +48,7 @@ #include "rtc_base/string_encode.h" #include "rtc_base/third_party/base64/base64.h" #include "rtc_base/thread.h" +#include "test/gmock.h" #include "test/gtest.h" using cricket::ConnectionInfo; @@ -83,7 +89,6 @@ class FakeAudioProcessor : public AudioProcessorInterface { AudioProcessorInterface::AudioProcessorStatistics GetStats( bool has_recv_streams) override { AudioProcessorStatistics stats; - stats.typing_noise_detected = true; if (has_recv_streams) { stats.apm_statistics.echo_return_loss = 2.0; stats.apm_statistics.echo_return_loss_enhancement = 3.0; @@ -126,7 +131,6 @@ class FakeAudioProcessorWithInitValue : public AudioProcessorInterface { AudioProcessorInterface::AudioProcessorStatistics GetStats( bool /*has_recv_streams*/) override { AudioProcessorStatistics stats; - stats.typing_noise_detected = false; return stats; } }; @@ -482,10 +486,6 @@ void VerifyVoiceSenderInfoReport(const StatsReport* report, EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameAudioInputLevel, &value_in_report)); EXPECT_EQ(rtc::ToString(sinfo.audio_level), value_in_report); - EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameTypingNoiseState, - &value_in_report)); - std::string typing_detected = sinfo.typing_noise_detected ? "true" : "false"; - EXPECT_EQ(typing_detected, value_in_report); EXPECT_TRUE(GetValue(report, StatsReport::kStatsValueNameAnaBitrateActionCounter, &value_in_report)); @@ -545,7 +545,6 @@ void InitVoiceSenderInfo(cricket::VoiceSenderInfo* voice_sender_info, voice_sender_info->apm_statistics.echo_return_loss_enhancement = 109; voice_sender_info->apm_statistics.delay_median_ms = 110; voice_sender_info->apm_statistics.delay_standard_deviation_ms = 111; - voice_sender_info->typing_noise_detected = false; voice_sender_info->ana_statistics.bitrate_action_counter = 112; voice_sender_info->ana_statistics.channel_action_counter = 113; voice_sender_info->ana_statistics.dtx_action_counter = 114; @@ -562,8 +561,6 @@ void UpdateVoiceSenderInfoFromAudioTrack( audio_track->GetSignalLevel(&voice_sender_info->audio_level); AudioProcessorInterface::AudioProcessorStatistics audio_processor_stats = audio_track->GetAudioProcessor()->GetStats(has_remote_tracks); - voice_sender_info->typing_noise_detected = - audio_processor_stats.typing_noise_detected; voice_sender_info->apm_statistics = audio_processor_stats.apm_statistics; } @@ -669,7 +666,7 @@ class StatsCollectorTest : public ::testing::Test { const std::string kTransportName = "transport"; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); pc->AddVoiceChannel("audio", kTransportName); @@ -764,9 +761,8 @@ static rtc::scoped_refptr CreateMockReceiver( Return(track->kind() == MediaStreamTrackInterface::kAudioKind ? cricket::MEDIA_TYPE_AUDIO : cricket::MEDIA_TYPE_VIDEO)); - EXPECT_CALL(*receiver, SetMediaChannel(_)).Times(AtMost(1)); - EXPECT_CALL(*receiver, Stop()); - EXPECT_CALL(*receiver, StopAndEndTrack()); + EXPECT_CALL(*receiver, SetMediaChannel(_)).WillRepeatedly(Return()); + EXPECT_CALL(*receiver, Stop()).WillRepeatedly(Return()); return receiver; } @@ -778,32 +774,32 @@ class StatsCollectorTrackTest : public StatsCollectorTest, // stream, which is created if necessary. void AddOutgoingVideoTrack(FakePeerConnectionForStats* pc, StatsCollectorForTest* stats) { - track_ = VideoTrack::Create(kLocalTrackId, FakeVideoTrackSource::Create(), - rtc::Thread::Current()); + video_track_ = VideoTrack::Create( + kLocalTrackId, FakeVideoTrackSource::Create(), rtc::Thread::Current()); if (GetParam()) { if (!stream_) stream_ = MediaStream::Create("streamid"); - stream_->AddTrack(track_); - stats->AddStream(stream_); + stream_->AddTrack(video_track()); + stats->AddStream(stream_.get()); } else { - stats->AddTrack(track_); + stats->AddTrack(video_track_.get()); } - pc->AddSender(CreateMockSender(track_, kSsrcOfTrack)); + pc->AddSender(CreateMockSender(video_track_, kSsrcOfTrack)); } // Adds a incoming video track with a given SSRC into the stats. void AddIncomingVideoTrack(FakePeerConnectionForStats* pc, StatsCollectorForTest* stats) { - track_ = VideoTrack::Create(kRemoteTrackId, FakeVideoTrackSource::Create(), - rtc::Thread::Current()); + video_track_ = VideoTrack::Create( + kRemoteTrackId, FakeVideoTrackSource::Create(), rtc::Thread::Current()); if (GetParam()) { stream_ = MediaStream::Create("streamid"); - stream_->AddTrack(track_); - stats->AddStream(stream_); + stream_->AddTrack(video_track()); + stats->AddStream(stream_.get()); } else { - stats->AddTrack(track_); + stats->AddTrack(video_track_.get()); } - pc->AddReceiver(CreateMockReceiver(track_, kSsrcOfTrack)); + pc->AddReceiver(CreateMockReceiver(video_track_, kSsrcOfTrack)); } // Adds a outgoing audio track with a given SSRC into the stats, @@ -817,10 +813,10 @@ class StatsCollectorTrackTest : public StatsCollectorTest, if (GetParam()) { if (!stream_) stream_ = MediaStream::Create("streamid"); - stream_->AddTrack(audio_track_); - stats->AddStream(stream_); + stream_->AddTrack(audio_track()); + stats->AddStream(stream_.get()); } else { - stats->AddTrack(audio_track_); + stats->AddTrack(audio_track_.get()); } return pc->AddSender(CreateMockSender(audio_track_, kSsrcOfTrack)); } @@ -830,24 +826,27 @@ class StatsCollectorTrackTest : public StatsCollectorTest, StatsCollectorForTest* stats) { audio_track_ = rtc::make_ref_counted(kRemoteTrackId); if (GetParam()) { - if (stream_ == NULL) + if (stream_ == nullptr) stream_ = MediaStream::Create("streamid"); - stream_->AddTrack(audio_track_); - stats->AddStream(stream_); + stream_->AddTrack(audio_track()); + stats->AddStream(stream_.get()); } else { - stats->AddTrack(audio_track_); + stats->AddTrack(audio_track_.get()); } pc->AddReceiver(CreateMockReceiver(audio_track_, kSsrcOfTrack)); } + rtc::scoped_refptr audio_track() { return audio_track_; } + rtc::scoped_refptr video_track() { return video_track_; } + rtc::scoped_refptr stream_; - rtc::scoped_refptr track_; + rtc::scoped_refptr video_track_; rtc::scoped_refptr audio_track_; }; TEST_F(StatsCollectorTest, FilterOutNegativeDataChannelId) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); pc->AddSctpDataChannel("hacks"); @@ -871,7 +870,7 @@ TEST_F(StatsCollectorTest, ExtractDataInfo) { DataChannelInterface::DataState::kConnecting); auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); InternalDataChannelInit init; init.id = kDataChannelId; @@ -910,7 +909,7 @@ TEST_P(StatsCollectorTrackTest, BytesCounterHandles64Bits) { constexpr int64_t kBytesSent = 12345678901234LL; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VideoSenderInfo video_sender_info; video_sender_info.add_ssrc(1234); @@ -921,7 +920,7 @@ TEST_P(StatsCollectorTrackTest, BytesCounterHandles64Bits) { pc->AddVideoChannel("video", "transport", video_info); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -942,7 +941,7 @@ TEST_P(StatsCollectorTrackTest, AudioBandwidthEstimationInfoIsReported) { constexpr int kPacerDelay = 123; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VoiceSenderInfo voice_sender_info; voice_sender_info.add_ssrc(1234); @@ -954,7 +953,7 @@ TEST_P(StatsCollectorTrackTest, AudioBandwidthEstimationInfoIsReported) { auto* voice_media_channel = pc->AddVoiceChannel("audio", "transport"); voice_media_channel->SetStats(voice_info); - AddOutgoingAudioTrack(pc, stats.get()); + AddOutgoingAudioTrack(pc.get(), stats.get()); Call::Stats call_stats; call_stats.send_bandwidth_bps = kSendBandwidth; @@ -991,7 +990,7 @@ TEST_P(StatsCollectorTrackTest, VideoBandwidthEstimationInfoIsReported) { constexpr int kPacerDelay = 123; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VideoSenderInfo video_sender_info; video_sender_info.add_ssrc(1234); @@ -1003,7 +1002,7 @@ TEST_P(StatsCollectorTrackTest, VideoBandwidthEstimationInfoIsReported) { pc->AddVideoChannel("video", "transport", video_info); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); Call::Stats call_stats; call_stats.send_bandwidth_bps = kSendBandwidth; @@ -1034,7 +1033,7 @@ TEST_P(StatsCollectorTrackTest, VideoBandwidthEstimationInfoIsReported) { // exists in the returned stats. TEST_F(StatsCollectorTest, SessionObjectExists) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1048,7 +1047,7 @@ TEST_F(StatsCollectorTest, SessionObjectExists) { // in the returned stats. TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); @@ -1065,10 +1064,10 @@ TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) { // without calling StatsCollector::UpdateStats. TEST_P(StatsCollectorTrackTest, TrackObjectExistsWithoutUpdateStats) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); pc->AddVideoChannel("video", "transport"); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); // Verfies the existence of the track report. StatsReports reports; @@ -1089,7 +1088,7 @@ TEST_P(StatsCollectorTrackTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { constexpr int64_t kBytesSent = 12345678901234LL; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VideoSenderInfo video_sender_info; video_sender_info.add_ssrc(1234); @@ -1100,7 +1099,7 @@ TEST_P(StatsCollectorTrackTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { pc->AddVideoChannel("video", "transport", video_info); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1115,7 +1114,7 @@ TEST_P(StatsCollectorTrackTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) { // Get report for the specific `track`. reports.clear(); - stats->GetStats(track_, &reports); + stats->GetStats(video_track_.get(), &reports); // `reports` should contain at least one session report, one track report, // and one ssrc report. EXPECT_LE(3u, reports.size()); @@ -1143,7 +1142,7 @@ TEST_P(StatsCollectorTrackTest, TransportObjectLinkedFromSsrcObject) { constexpr int64_t kBytesSent = 12345678901234LL; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VideoSenderInfo video_sender_info; video_sender_info.add_ssrc(1234); @@ -1154,7 +1153,7 @@ TEST_P(StatsCollectorTrackTest, TransportObjectLinkedFromSsrcObject) { pc->AddVideoChannel("video", "transport", video_info); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1185,10 +1184,10 @@ TEST_P(StatsCollectorTrackTest, TransportObjectLinkedFromSsrcObject) { // an outgoing SSRC where remote stats are not returned. TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsAbsent) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); pc->AddVideoChannel("video", "transport"); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1203,7 +1202,7 @@ TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsAbsent) { // an outgoing SSRC where stats are returned. TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsPresent) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); SsrcReceiverInfo remote_ssrc_stats; remote_ssrc_stats.timestamp = 12345.678; @@ -1216,7 +1215,7 @@ TEST_P(StatsCollectorTrackTest, RemoteSsrcInfoIsPresent) { pc->AddVideoChannel("video", "transport", video_info); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1234,7 +1233,7 @@ TEST_P(StatsCollectorTrackTest, ReportsFromRemoteTrack) { constexpr int64_t kNumOfPacketsConcealed = 54321; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); VideoReceiverInfo video_receiver_info; video_receiver_info.add_ssrc(1234); @@ -1244,7 +1243,7 @@ TEST_P(StatsCollectorTrackTest, ReportsFromRemoteTrack) { pc->AddVideoChannel("video", "transport", video_info); - AddIncomingVideoTrack(pc, stats.get()); + AddIncomingVideoTrack(pc.get(), stats.get()); stats->UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard); StatsReports reports; @@ -1283,7 +1282,7 @@ TEST_F(StatsCollectorTest, IceCandidateReport) { const rtc::SocketAddress kRemoteAddress(kRemoteIp, kRemotePort); auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); cricket::Candidate local; EXPECT_GT(local.id().length(), 0u); @@ -1419,7 +1418,7 @@ TEST_F(StatsCollectorTest, ChainlessCertificateReportsCreated) { // transport is present. TEST_F(StatsCollectorTest, NoTransport) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); // This will cause the fake PeerConnection to generate a TransportStats entry // but with only a single dummy TransportChannelStats. @@ -1478,28 +1477,28 @@ TEST_P(StatsCollectorTrackTest, FilterOutNegativeInitialValues) { } auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); // Create a local stream with a local audio track and adds it to the stats. stream_ = MediaStream::Create("streamid"); auto local_track = rtc::make_ref_counted(kLocalTrackId); - stream_->AddTrack(local_track); + stream_->AddTrack(rtc::scoped_refptr(local_track.get())); pc->AddSender(CreateMockSender(local_track, kSsrcOfTrack)); if (GetParam()) { - stats->AddStream(stream_); + stats->AddStream(stream_.get()); } stats->AddLocalAudioTrack(local_track.get(), kSsrcOfTrack); // Create a remote stream with a remote audio track and adds it to the stats. rtc::scoped_refptr remote_stream( MediaStream::Create("remotestreamid")); - auto remote_track = + rtc::scoped_refptr remote_track = rtc::make_ref_counted(kRemoteTrackId); remote_stream->AddTrack(remote_track); pc->AddReceiver(CreateMockReceiver(remote_track, kSsrcOfTrack)); if (GetParam()) { - stats->AddStream(remote_stream); + stats->AddStream(remote_stream.get()); } VoiceSenderInfo voice_sender_info; @@ -1565,14 +1564,15 @@ TEST_P(StatsCollectorTrackTest, FilterOutNegativeInitialValues) { // AudioTrackInterface::GetStats() method. TEST_P(StatsCollectorTrackTest, GetStatsFromLocalAudioTrack) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); - AddOutgoingAudioTrack(pc, stats.get()); - stats->AddLocalAudioTrack(audio_track_, kSsrcOfTrack); + AddOutgoingAudioTrack(pc.get(), stats.get()); + stats->AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); VoiceSenderInfo voice_sender_info; InitVoiceSenderInfo(&voice_sender_info); - UpdateVoiceSenderInfoFromAudioTrack(audio_track_, &voice_sender_info, false); + UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info, + false); VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); @@ -1580,7 +1580,7 @@ TEST_P(StatsCollectorTrackTest, GetStatsFromLocalAudioTrack) { voice_media_channel->SetStats(voice_info); StatsReports reports; // returned values. - VerifyAudioTrackStats(audio_track_, stats.get(), voice_info, &reports); + VerifyAudioTrackStats(audio_track_.get(), stats.get(), voice_info, &reports); // Verify that there is no remote report for the local audio track because // we did not set it up. @@ -1593,9 +1593,9 @@ TEST_P(StatsCollectorTrackTest, GetStatsFromLocalAudioTrack) { // correctly. TEST_P(StatsCollectorTrackTest, GetStatsFromRemoteStream) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); - AddIncomingAudioTrack(pc, stats.get()); + AddIncomingAudioTrack(pc.get(), stats.get()); VoiceReceiverInfo voice_receiver_info; InitVoiceReceiverInfo(&voice_receiver_info); @@ -1607,16 +1607,16 @@ TEST_P(StatsCollectorTrackTest, GetStatsFromRemoteStream) { voice_media_channel->SetStats(voice_info); StatsReports reports; // returned values. - VerifyAudioTrackStats(audio_track_, stats.get(), voice_info, &reports); + VerifyAudioTrackStats(audio_track_.get(), stats.get(), voice_info, &reports); } // This test verifies that a local stats object won't update its statistics // after a RemoveLocalAudioTrack() call. TEST_P(StatsCollectorTrackTest, GetStatsAfterRemoveAudioStream) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); - AddOutgoingAudioTrack(pc, stats.get()); + AddOutgoingAudioTrack(pc.get(), stats.get()); stats->AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); VoiceSenderInfo voice_sender_info; @@ -1655,19 +1655,20 @@ TEST_P(StatsCollectorTrackTest, GetStatsAfterRemoveAudioStream) { // the same ssrc, they populate stats reports correctly. TEST_P(StatsCollectorTrackTest, LocalAndRemoteTracksWithSameSsrc) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); // Create a local stream with a local audio track and adds it to the stats. - AddOutgoingAudioTrack(pc, stats.get()); + AddOutgoingAudioTrack(pc.get(), stats.get()); stats->AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); // Create a remote stream with a remote audio track and adds it to the stats. rtc::scoped_refptr remote_stream( MediaStream::Create("remotestreamid")); - auto remote_track = rtc::make_ref_counted(kRemoteTrackId); + rtc::scoped_refptr remote_track = + rtc::make_ref_counted(kRemoteTrackId); pc->AddReceiver(CreateMockReceiver(remote_track, kSsrcOfTrack)); remote_stream->AddTrack(remote_track); - stats->AddStream(remote_stream); + stats->AddStream(remote_stream.get()); VoiceSenderInfo voice_sender_info; InitVoiceSenderInfo(&voice_sender_info); @@ -1727,15 +1728,16 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { } auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); // Create a local stream with a local audio track and adds it to the stats. - auto sender = AddOutgoingAudioTrack(pc, stats.get()); - stats->AddLocalAudioTrack(audio_track_, kSsrcOfTrack); + auto sender = AddOutgoingAudioTrack(pc.get(), stats.get()); + stats->AddLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); VoiceSenderInfo voice_sender_info; InitVoiceSenderInfo(&voice_sender_info); - UpdateVoiceSenderInfoFromAudioTrack(audio_track_, &voice_sender_info, false); + UpdateVoiceSenderInfoFromAudioTrack(audio_track_.get(), &voice_sender_info, + false); voice_sender_info.add_ssrc(kSsrcOfTrack); VoiceMediaInfo voice_info; voice_info.senders.push_back(voice_sender_info); @@ -1744,10 +1746,10 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { voice_media_channel->SetStats(voice_info); StatsReports reports; // returned values. - VerifyAudioTrackStats(audio_track_, stats.get(), voice_info, &reports); + VerifyAudioTrackStats(audio_track_.get(), stats.get(), voice_info, &reports); // Remove the previous audio track from the stream. - stream_->RemoveTrack(audio_track_.get()); + stream_->RemoveTrack(audio_track()); stats->RemoveLocalAudioTrack(audio_track_.get(), kSsrcOfTrack); pc->RemoveSender(sender); @@ -1755,21 +1757,23 @@ TEST_P(StatsCollectorTrackTest, TwoLocalTracksWithSameSsrc) { static const std::string kNewTrackId = "new_track_id"; auto new_audio_track = rtc::make_ref_counted(kNewTrackId); pc->AddSender(CreateMockSender(new_audio_track, kSsrcOfTrack)); - stream_->AddTrack(new_audio_track); + stream_->AddTrack( + rtc::scoped_refptr(new_audio_track.get())); - stats->AddLocalAudioTrack(new_audio_track, kSsrcOfTrack); + stats->AddLocalAudioTrack(new_audio_track.get(), kSsrcOfTrack); stats->InvalidateCache(); VoiceSenderInfo new_voice_sender_info; InitVoiceSenderInfo(&new_voice_sender_info); - UpdateVoiceSenderInfoFromAudioTrack(new_audio_track, &new_voice_sender_info, - false); + UpdateVoiceSenderInfoFromAudioTrack(new_audio_track.get(), + &new_voice_sender_info, false); VoiceMediaInfo new_voice_info; new_voice_info.senders.push_back(new_voice_sender_info); voice_media_channel->SetStats(new_voice_info); reports.clear(); - VerifyAudioTrackStats(new_audio_track, stats.get(), new_voice_info, &reports); + VerifyAudioTrackStats(new_audio_track.get(), stats.get(), new_voice_info, + &reports); } // Test that if there are two local senders with the same track then two SSRC @@ -1780,7 +1784,7 @@ TEST_P(StatsCollectorTrackTest, TwoLocalSendersWithSameTrack) { constexpr uint32_t kSecondSsrc = 33; auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); auto local_track = rtc::make_ref_counted(kLocalTrackId); @@ -1840,9 +1844,9 @@ TEST_P(StatsCollectorTrackTest, TwoLocalSendersWithSameTrack) { // This test verifies that stats are correctly set in video send ssrc stats. TEST_P(StatsCollectorTrackTest, VerifyVideoSendSsrcStats) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); - AddOutgoingVideoTrack(pc, stats.get()); + AddOutgoingVideoTrack(pc.get(), stats.get()); VideoSenderInfo video_sender_info; video_sender_info.add_ssrc(1234); @@ -1867,9 +1871,9 @@ TEST_P(StatsCollectorTrackTest, VerifyVideoSendSsrcStats) { // This test verifies that stats are correctly set in video receive ssrc stats. TEST_P(StatsCollectorTrackTest, VerifyVideoReceiveSsrcStatsNew) { auto pc = CreatePeerConnection(); - auto stats = CreateStatsCollector(pc); + auto stats = CreateStatsCollector(pc.get()); - AddIncomingVideoTrack(pc, stats.get()); + AddIncomingVideoTrack(pc.get(), stats.get()); VideoReceiverInfo video_receiver_info; video_receiver_info.add_ssrc(1234); diff --git a/pc/stream_collection.h b/pc/stream_collection.h index ed85947bf5..f0f3f07b4b 100644 --- a/pc/stream_collection.h +++ b/pc/stream_collection.h @@ -34,14 +34,14 @@ class StreamCollection : public StreamCollectionInterface { virtual size_t count() { return media_streams_.size(); } virtual MediaStreamInterface* at(size_t index) { - return media_streams_.at(index); + return media_streams_.at(index).get(); } virtual MediaStreamInterface* find(const std::string& id) { for (StreamVector::iterator it = media_streams_.begin(); it != media_streams_.end(); ++it) { if ((*it)->id().compare(id) == 0) { - return (*it); + return (*it).get(); } } return NULL; @@ -49,7 +49,8 @@ class StreamCollection : public StreamCollectionInterface { virtual MediaStreamTrackInterface* FindAudioTrack(const std::string& id) { for (size_t i = 0; i < media_streams_.size(); ++i) { - MediaStreamTrackInterface* track = media_streams_[i]->FindAudioTrack(id); + MediaStreamTrackInterface* track = + media_streams_[i]->FindAudioTrack(id).get(); if (track) { return track; } @@ -59,7 +60,8 @@ class StreamCollection : public StreamCollectionInterface { virtual MediaStreamTrackInterface* FindVideoTrack(const std::string& id) { for (size_t i = 0; i < media_streams_.size(); ++i) { - MediaStreamTrackInterface* track = media_streams_[i]->FindVideoTrack(id); + MediaStreamTrackInterface* track = + media_streams_[i]->FindVideoTrack(id).get(); if (track) { return track; } diff --git a/pc/test/DEPS b/pc/test/DEPS index fff21d98ea..33e6d94b25 100644 --- a/pc/test/DEPS +++ b/pc/test/DEPS @@ -1,6 +1,5 @@ include_rules = [ - # Allow include of Chrome base/android to allow inclusion of headers needed - # for accessing the JVM and Application context in gtest. - "+base/android", + # Allow include of sdk/android to allow accessing the JVM and Env in tests. + "+sdk/android", "+modules/utility/include/jvm_android.h", ] diff --git a/pc/test/android_test_initializer.cc b/pc/test/android_test_initializer.cc index 584aedaaed..963544cb4b 100644 --- a/pc/test/android_test_initializer.cc +++ b/pc/test/android_test_initializer.cc @@ -10,22 +10,14 @@ #include "pc/test/android_test_initializer.h" +#include #include - -#include "rtc_base/ignore_wundef.h" - -// Note: this dependency is dangerous since it reaches into Chromium's base. -// There's a risk of e.g. macro clashes. This file may only be used in tests. -// Since we use Chromes build system for creating the gtest binary, this should -// be fine. -RTC_PUSH_IGNORING_WUNDEF() -#include "base/android/jni_android.h" -RTC_POP_IGNORING_WUNDEF() +#include #include "modules/utility/include/jvm_android.h" #include "rtc_base/checks.h" - -// TODO(phoglund): This include is also to a target we can't really depend on. +#include "sdk/android/src/jni/jvm.h" +// TODO(phoglund): This include is to a target we can't really depend on. // We need to either break it out into a smaller target or find some way to // not use it. #include "rtc_base/ssl_adapter.h" @@ -40,8 +32,8 @@ static pthread_once_t g_initialize_once = PTHREAD_ONCE_INIT; // C++ runner binary, we want to initialize the same global objects we normally // do if this had been a Java binary. void EnsureInitializedOnce() { - RTC_CHECK(::base::android::IsVMInitialized()); - JNIEnv* jni = ::base::android::AttachCurrentThread(); + RTC_CHECK(::webrtc::jni::GetJVM() != nullptr); + JNIEnv* jni = ::webrtc::jni::AttachCurrentThreadIfNeeded(); JavaVM* jvm = NULL; RTC_CHECK_EQ(0, jni->GetJavaVM(&jvm)); diff --git a/pc/test/fake_data_channel_provider.h b/pc/test/fake_data_channel_controller.h similarity index 94% rename from pc/test/fake_data_channel_provider.h rename to pc/test/fake_data_channel_controller.h index f9e9e91d48..bdab7d2ec9 100644 --- a/pc/test/fake_data_channel_provider.h +++ b/pc/test/fake_data_channel_controller.h @@ -8,23 +8,23 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef PC_TEST_FAKE_DATA_CHANNEL_PROVIDER_H_ -#define PC_TEST_FAKE_DATA_CHANNEL_PROVIDER_H_ +#ifndef PC_TEST_FAKE_DATA_CHANNEL_CONTROLLER_H_ +#define PC_TEST_FAKE_DATA_CHANNEL_CONTROLLER_H_ #include #include "pc/sctp_data_channel.h" #include "rtc_base/checks.h" -class FakeDataChannelProvider - : public webrtc::SctpDataChannelProviderInterface { +class FakeDataChannelController + : public webrtc::SctpDataChannelControllerInterface { public: - FakeDataChannelProvider() + FakeDataChannelController() : send_blocked_(false), transport_available_(false), ready_to_send_(false), transport_error_(false) {} - virtual ~FakeDataChannelProvider() {} + virtual ~FakeDataChannelController() {} bool SendData(int sid, const webrtc::SendDataParams& params, @@ -157,4 +157,4 @@ class FakeDataChannelProvider std::set send_ssrcs_; std::set recv_ssrcs_; }; -#endif // PC_TEST_FAKE_DATA_CHANNEL_PROVIDER_H_ +#endif // PC_TEST_FAKE_DATA_CHANNEL_CONTROLLER_H_ diff --git a/pc/test/fake_peer_connection_base.h b/pc/test/fake_peer_connection_base.h index 7d64ab8180..f629f04e6b 100644 --- a/pc/test/fake_peer_connection_base.h +++ b/pc/test/fake_peer_connection_base.h @@ -17,8 +17,10 @@ #include #include +#include "api/field_trials_view.h" #include "api/sctp_transport_interface.h" #include "pc/peer_connection_internal.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -358,7 +360,10 @@ class FakePeerConnectionBase : public PeerConnectionInternal { void SetSctpDataMid(const std::string& mid) override {} void ResetSctpDataMid() override {} + const FieldTrialsView& trials() const override { return field_trials_; } + protected: + webrtc::test::ScopedKeyValueConfig field_trials_; sigslot::signal1 SignalSctpDataChannelCreated_; }; diff --git a/pc/test/fake_peer_connection_for_stats.h b/pc/test/fake_peer_connection_for_stats.h index 4c1f73af8a..d7f97daf76 100644 --- a/pc/test/fake_peer_connection_for_stats.h +++ b/pc/test/fake_peer_connection_for_stats.h @@ -19,8 +19,10 @@ #include #include "media/base/fake_media_engine.h" +#include "pc/channel.h" +#include "pc/channel_manager.h" #include "pc/stream_collection.h" -#include "pc/test/fake_data_channel_provider.h" +#include "pc/test/fake_data_channel_controller.h" #include "pc/test/fake_peer_connection_base.h" namespace webrtc { @@ -154,7 +156,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { ~FakePeerConnectionForStats() { for (auto transceiver : transceivers_) { - transceiver->internal()->SetChannel(nullptr, nullptr); + transceiver->internal()->ClearChannel(); } } @@ -180,7 +182,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { void RemoveSender(rtc::scoped_refptr sender) { GetOrCreateFirstTransceiverOfType(sender->media_type()) ->internal() - ->RemoveSender(sender); + ->RemoveSender(sender.get()); } rtc::scoped_refptr AddReceiver( @@ -198,25 +200,24 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { void RemoveReceiver(rtc::scoped_refptr receiver) { GetOrCreateFirstTransceiverOfType(receiver->media_type()) ->internal() - ->RemoveReceiver(receiver); + ->RemoveReceiver(receiver.get()); } FakeVoiceMediaChannelForStats* AddVoiceChannel( const std::string& mid, const std::string& transport_name, cricket::VoiceMediaInfo initial_stats = cricket::VoiceMediaInfo()) { - RTC_DCHECK(!voice_channel_); auto voice_media_channel = std::make_unique(network_thread_); auto* voice_media_channel_ptr = voice_media_channel.get(); - voice_channel_ = std::make_unique( + auto voice_channel = std::make_unique( worker_thread_, network_thread_, signaling_thread_, std::move(voice_media_channel), mid, kDefaultSrtpRequired, webrtc::CryptoOptions(), &channel_manager_.ssrc_generator(), transport_name); GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO) ->internal() - ->SetChannel(voice_channel_.get(), + ->SetChannel(std::move(voice_channel), [](const std::string&) { return nullptr; }); voice_media_channel_ptr->SetStats(initial_stats); return voice_media_channel_ptr; @@ -226,18 +227,17 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { const std::string& mid, const std::string& transport_name, cricket::VideoMediaInfo initial_stats = cricket::VideoMediaInfo()) { - RTC_DCHECK(!video_channel_); auto video_media_channel = std::make_unique(network_thread_); auto video_media_channel_ptr = video_media_channel.get(); - video_channel_ = std::make_unique( + auto video_channel = std::make_unique( worker_thread_, network_thread_, signaling_thread_, std::move(video_media_channel), mid, kDefaultSrtpRequired, webrtc::CryptoOptions(), &channel_manager_.ssrc_generator(), transport_name); GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO) ->internal() - ->SetChannel(video_channel_.get(), + ->SetChannel(std::move(video_channel), [](const std::string&) { return nullptr; }); video_media_channel_ptr->SetStats(initial_stats); return video_media_channel_ptr; @@ -250,7 +250,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { void AddSctpDataChannel(const std::string& label, const InternalDataChannelInit& init) { // TODO(bugs.webrtc.org/11547): Supply a separate network thread. - AddSctpDataChannel(SctpDataChannel::Create(&data_channel_provider_, label, + AddSctpDataChannel(SctpDataChannel::Create(&data_channel_controller_, label, init, rtc::Thread::Current(), rtc::Thread::Current())); } @@ -406,7 +406,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { } } auto transceiver = RtpTransceiverProxyWithInternal::Create( - signaling_thread_, new RtpTransceiver(media_type, &channel_manager_)); + signaling_thread_, + rtc::make_ref_counted(media_type, &channel_manager_)); transceivers_.push_back(transceiver); return transceiver; } @@ -415,10 +416,6 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { public: TestChannelManager(rtc::Thread* worker, rtc::Thread* network) : cricket::ChannelManager(nullptr, true, worker, network) {} - - // Override DestroyChannel so that calls from the transceiver won't go to - // the default ChannelManager implementation. - void DestroyChannel(cricket::ChannelInterface*) override {} }; rtc::Thread* const network_thread_; @@ -434,10 +431,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase { rtc::scoped_refptr>> transceivers_; - FakeDataChannelProvider data_channel_provider_; - - std::unique_ptr voice_channel_; - std::unique_ptr video_channel_; + FakeDataChannelController data_channel_controller_; std::vector> sctp_data_channels_; diff --git a/pc/test/integration_test_helpers.cc b/pc/test/integration_test_helpers.cc index 10e4f455ba..3f07f361fc 100644 --- a/pc/test/integration_test_helpers.cc +++ b/pc/test/integration_test_helpers.cc @@ -56,4 +56,46 @@ int FindFirstMediaStatsIndexByKind( return -1; } +TaskQueueMetronome::TaskQueueMetronome(TaskQueueFactory* factory, + TimeDelta tick_period) + : tick_period_(tick_period), + queue_(factory->CreateTaskQueue("MetronomeQueue", + TaskQueueFactory::Priority::HIGH)) { + tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] { + MutexLock lock(&mutex_); + for (auto* listener : listeners_) { + listener->OnTickTaskQueue()->PostTask( + ToQueuedTask([listener] { listener->OnTick(); })); + } + return tick_period_; + }); +} + +TaskQueueMetronome::~TaskQueueMetronome() { + RTC_DCHECK(listeners_.empty()); + rtc::Event stop_event; + queue_.PostTask([this, &stop_event] { + tick_task_.Stop(); + stop_event.Set(); + }); + stop_event.Wait(1000); +} + +void TaskQueueMetronome::AddListener(TickListener* listener) { + MutexLock lock(&mutex_); + auto [it, inserted] = listeners_.insert(listener); + RTC_DCHECK(inserted); +} + +void TaskQueueMetronome::RemoveListener(TickListener* listener) { + MutexLock lock(&mutex_); + auto it = listeners_.find(listener); + RTC_DCHECK(it != listeners_.end()); + listeners_.erase(it); +} + +TimeDelta TaskQueueMetronome::TickPeriod() const { + return tick_period_; +} + } // namespace webrtc diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h index 891d1ddae4..33f0404443 100644 --- a/pc/test/integration_test_helpers.h +++ b/pc/test/integration_test_helpers.h @@ -26,12 +26,14 @@ #include #include "absl/algorithm/container.h" +#include "absl/memory/memory.h" #include "absl/types/optional.h" #include "api/audio_options.h" #include "api/call/call_factory_interface.h" #include "api/candidate.h" #include "api/crypto/crypto_options.h" #include "api/data_channel_interface.h" +#include "api/field_trials_view.h" #include "api/ice_transport_interface.h" #include "api/jsep.h" #include "api/media_stream_interface.h" @@ -51,7 +53,6 @@ #include "api/task_queue/default_task_queue_factory.h" #include "api/task_queue/task_queue_factory.h" #include "api/transport/field_trial_based_config.h" -#include "api/transport/webrtc_key_value_config.h" #include "api/uma_metrics.h" #include "api/video/video_rotation.h" #include "api/video_codecs/sdp_video_format.h" @@ -94,6 +95,7 @@ #include "pc/test/mock_peer_connection_observers.h" #include "pc/video_track_source.h" #include "rtc_base/checks.h" +#include "rtc_base/event.h" #include "rtc_base/fake_clock.h" #include "rtc_base/fake_mdns_responder.h" #include "rtc_base/fake_network.h" @@ -110,14 +112,16 @@ #include "rtc_base/socket_address.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" +#include "rtc_base/task_utils/repeating_task.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/test_certificate_verifier.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" #include "rtc_base/time_utils.h" #include "rtc_base/virtual_socket_server.h" #include "system_wrappers/include/metrics.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" namespace webrtc { @@ -171,6 +175,24 @@ int FindFirstMediaStatsIndexByKind( const std::vector& media_stats_vec); +class TaskQueueMetronome : public webrtc::Metronome { + public: + TaskQueueMetronome(TaskQueueFactory* factory, TimeDelta tick_period); + ~TaskQueueMetronome() override; + + // webrtc::Metronome implementation. + void AddListener(TickListener* listener) override; + void RemoveListener(TickListener* listener) override; + TimeDelta TickPeriod() const override; + + private: + Mutex mutex_; + const TimeDelta tick_period_; + std::set listeners_ RTC_GUARDED_BY(mutex_); + RepeatingTaskHandle tick_task_; + rtc::TaskQueue queue_; +}; + class SignalingMessageReceiver { public: virtual void ReceiveSdpMessage(SdpType type, const std::string& msg) = 0; @@ -324,7 +346,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, // TODO(perkj): Test audio source when it is implemented. Currently audio // always use the default input. return peer_connection_factory_->CreateAudioTrack(rtc::CreateRandomUuid(), - source); + source.get()); } rtc::scoped_refptr CreateLocalVideoTrack() { @@ -388,10 +410,12 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, void CreateDataChannel(const std::string& label, const webrtc::DataChannelInit* init) { - data_channels_.push_back(pc()->CreateDataChannel(label, init)); + auto data_channel_or_error = pc()->CreateDataChannelOrError(label, init); + ASSERT_TRUE(data_channel_or_error.ok()); + data_channels_.push_back(data_channel_or_error.MoveValue()); ASSERT_TRUE(data_channels_.back().get() != nullptr); data_observers_.push_back( - std::make_unique(data_channels_.back())); + std::make_unique(data_channels_.back().get())); } // Return the last observed data channel. @@ -399,10 +423,10 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, if (data_channels_.size() == 0) { return nullptr; } - return data_channels_.back(); + return data_channels_.back().get(); } // Return all data channels. - const std::vector>& data_channels() { + std::vector>& data_channels() { return data_channels_; } @@ -413,6 +437,10 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, return data_observers_.back().get(); } + std::vector>& data_observers() { + return data_observers_; + } + int audio_frames_received() const { return fake_audio_capture_module_->frames_received(); } @@ -442,7 +470,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, webrtc::MediaStreamTrackInterface* track) { auto observer = rtc::make_ref_counted(); EXPECT_TRUE(peer_connection_->GetStats( - observer, nullptr, PeerConnectionInterface::kStatsOutputLevelStandard)); + observer.get(), nullptr, + PeerConnectionInterface::kStatsOutputLevelStandard)); EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout); return observer; } @@ -457,7 +486,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, rtc::scoped_refptr NewGetStats() { auto callback = rtc::make_ref_counted(); - peer_connection_->GetStats(callback); + peer_connection_->GetStats(callback.get()); EXPECT_TRUE_WAIT(callback->called(), kDefaultTimeout); return callback->report(); } @@ -518,7 +547,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, ADD_FAILURE(); return nullptr; } - return pc()->remote_streams(); + return pc()->remote_streams().get(); } StreamCollectionInterface* local_streams() { @@ -526,7 +555,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, ADD_FAILURE(); return nullptr; } - return pc()->local_streams(); + return pc()->local_streams().get(); } webrtc::PeerConnectionInterface::SignalingState signaling_state() { @@ -595,8 +624,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, std::unique_ptr CreateOfferAndWait() { auto observer = rtc::make_ref_counted(); - pc()->CreateOffer(observer, offer_answer_options_); - return WaitForDescriptionFromObserver(observer); + pc()->CreateOffer(observer.get(), offer_answer_options_); + return WaitForDescriptionFromObserver(observer.get()); } bool Rollback() { return SetRemoteDescription( @@ -705,7 +734,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, rtc::Thread* worker_thread, std::unique_ptr event_log_factory, bool reset_encoder_factory, - bool reset_decoder_factory) { + bool reset_decoder_factory, + bool create_media_engine) { // There's an error in this test code if Init ends up being called twice. RTC_DCHECK(!peer_connection_); RTC_DCHECK(!peer_connection_factory_); @@ -729,6 +759,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, pc_factory_dependencies.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); pc_factory_dependencies.trials = std::make_unique(); + pc_factory_dependencies.metronome = std::make_unique( + pc_factory_dependencies.task_queue_factory.get(), TimeDelta::Millis(8)); cricket::MediaEngineDependencies media_deps; media_deps.task_queue_factory = pc_factory_dependencies.task_queue_factory.get(); @@ -750,8 +782,10 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, media_deps.trials = pc_factory_dependencies.trials.get(); - pc_factory_dependencies.media_engine = - cricket::CreateMediaEngine(std::move(media_deps)); + if (create_media_engine) { + pc_factory_dependencies.media_engine = + cricket::CreateMediaEngine(std::move(media_deps)); + } pc_factory_dependencies.call_factory = webrtc::CreateCallFactory(); if (event_log_factory) { event_log_factory_ = event_log_factory.get(); @@ -796,8 +830,11 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, modified_config.set_cpu_adaptation(false); dependencies.observer = this; - return peer_connection_factory_->CreatePeerConnection( - modified_config, std::move(dependencies)); + auto peer_connection_or_error = + peer_connection_factory_->CreatePeerConnectionOrError( + modified_config, std::move(dependencies)); + return peer_connection_or_error.ok() ? peer_connection_or_error.MoveValue() + : nullptr; } void set_signaling_message_receiver( @@ -822,9 +859,10 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, config, false /* remote */)); rtc::scoped_refptr track( peer_connection_factory_->CreateVideoTrack( - rtc::CreateRandomUuid(), video_track_sources_.back())); + rtc::CreateRandomUuid(), video_track_sources_.back().get())); if (!local_video_renderer_) { - local_video_renderer_.reset(new webrtc::FakeVideoTrackRenderer(track)); + local_video_renderer_.reset( + new webrtc::FakeVideoTrackRenderer(track.get())); } return track; } @@ -866,8 +904,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, std::unique_ptr CreateAnswer() { auto observer = rtc::make_ref_counted(); - pc()->CreateAnswer(observer, offer_answer_options_); - return WaitForDescriptionFromObserver(observer); + pc()->CreateAnswer(observer.get(), offer_answer_options_); + return WaitForDescriptionFromObserver(observer.get()); } std::unique_ptr WaitForDescriptionFromObserver( @@ -896,7 +934,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, std::string sdp; EXPECT_TRUE(desc->ToString(&sdp)); RTC_LOG(LS_INFO) << debug_name_ << ": local SDP contents=\n" << sdp; - pc()->SetLocalDescription(observer, desc.release()); + pc()->SetLocalDescription(observer.get(), desc.release()); RemoveUnusedVideoRenderers(); // As mentioned above, we need to send the message immediately after // SetLocalDescription. @@ -908,7 +946,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, bool SetRemoteDescription(std::unique_ptr desc) { auto observer = rtc::make_ref_counted(); RTC_LOG(LS_INFO) << debug_name_ << ": SetRemoteDescription"; - pc()->SetRemoteDescription(observer, desc.release()); + pc()->SetRemoteDescription(observer.get(), desc.release()); RemoveUnusedVideoRenderers(); EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout); return observer->result(); @@ -1002,9 +1040,12 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, int sdp_mline_index, const std::string& msg) override { RTC_LOG(LS_INFO) << debug_name_ << ": ReceiveIceMessage"; - std::unique_ptr candidate( - webrtc::CreateIceCandidate(sdp_mid, sdp_mline_index, msg, nullptr)); - EXPECT_TRUE(pc()->AddIceCandidate(candidate.get())); + absl::optional result; + pc()->AddIceCandidate(absl::WrapUnique(webrtc::CreateIceCandidate( + sdp_mid, sdp_mline_index, msg, nullptr)), + [&result](RTCError r) { result = r; }); + EXPECT_TRUE_WAIT(result.has_value(), kDefaultTimeout); + EXPECT_TRUE(result.value().ok()); } // PeerConnectionObserver callbacks. @@ -1022,7 +1063,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, ASSERT_TRUE(fake_video_renderers_.find(video_track->id()) == fake_video_renderers_.end()); fake_video_renderers_[video_track->id()] = - std::make_unique(video_track); + std::make_unique(video_track.get()); } } void OnRemoveTrack( @@ -1102,7 +1143,7 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, RTC_LOG(LS_INFO) << debug_name_ << ": OnDataChannel"; data_channels_.push_back(data_channel); data_observers_.push_back( - std::make_unique(data_channel)); + std::make_unique(data_channel.get())); } std::string debug_name_; @@ -1331,9 +1372,9 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { fss_(new rtc::FirewallSocketServer(ss_.get())), network_thread_(new rtc::Thread(fss_.get())), worker_thread_(rtc::Thread::Create()), - field_trials_(field_trials.has_value() - ? new test::ScopedFieldTrials(*field_trials) - : nullptr) { + // TODO(bugs.webrtc.org/10335): Pass optional ScopedKeyValueConfig. + field_trials_(new test::ScopedKeyValueConfig( + field_trials.has_value() ? *field_trials : "")) { network_thread_->SetName("PCNetworkThread", this); worker_thread_->SetName("PCWorkerThread", this); RTC_CHECK(network_thread_->Start()); @@ -1393,7 +1434,8 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { webrtc::PeerConnectionDependencies dependencies, std::unique_ptr event_log_factory, bool reset_encoder_factory, - bool reset_decoder_factory) { + bool reset_decoder_factory, + bool create_media_engine = true) { RTCConfiguration modified_config; if (config) { modified_config = *config; @@ -1409,7 +1451,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { if (!client->Init(options, &modified_config, std::move(dependencies), network_thread_.get(), worker_thread_.get(), std::move(event_log_factory), reset_encoder_factory, - reset_decoder_factory)) { + reset_decoder_factory, create_media_engine)) { return nullptr; } return client; @@ -1548,6 +1590,22 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { return caller_ && callee_; } + bool CreatePeerConnectionWrappersWithoutMediaEngine() { + caller_ = CreatePeerConnectionWrapper( + "Caller", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr), + nullptr, + /*reset_encoder_factory=*/false, + /*reset_decoder_factory=*/false, + /*create_media_engine=*/false); + callee_ = CreatePeerConnectionWrapper( + "Callee", nullptr, nullptr, webrtc::PeerConnectionDependencies(nullptr), + nullptr, + /*reset_encoder_factory=*/false, + /*reset_decoder_factory=*/false, + /*create_media_engine=*/false); + return caller_ && callee_; + } + cricket::TestTurnServer* CreateTurnServer( rtc::SocketAddress internal_address, rtc::SocketAddress external_address, @@ -1820,6 +1878,8 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { expected_cipher_suite); } + const FieldTrialsView& trials() const { return *field_trials_.get(); } + protected: SdpSemantics sdp_semantics_; @@ -1839,7 +1899,7 @@ class PeerConnectionIntegrationBaseTest : public ::testing::Test { std::vector> turn_customizers_; std::unique_ptr caller_; std::unique_ptr callee_; - std::unique_ptr field_trials_; + std::unique_ptr field_trials_; }; } // namespace webrtc diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h index 844a36e2d0..97e873e724 100644 --- a/pc/test/mock_channel_interface.h +++ b/pc/test/mock_channel_interface.h @@ -26,6 +26,8 @@ class MockChannelInterface : public cricket::ChannelInterface { public: MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); MOCK_METHOD(MediaChannel*, media_channel, (), (const, override)); + MOCK_METHOD(VoiceMediaChannel*, voice_media_channel, (), (const, override)); + MOCK_METHOD(VideoMediaChannel*, video_media_channel, (), (const, override)); MOCK_METHOD(absl::string_view, transport_name, (), (const, override)); MOCK_METHOD(const std::string&, mid, (), (const, override)); MOCK_METHOD(void, Enable, (bool), (override)); diff --git a/pc/test/mock_data_channel.h b/pc/test/mock_data_channel.h index ab4b0073da..f1c5374d28 100644 --- a/pc/test/mock_data_channel.h +++ b/pc/test/mock_data_channel.h @@ -18,7 +18,7 @@ namespace webrtc { -class MockSctpDataChannel : public rtc::RefCountedObject { +class MockSctpDataChannel : public SctpDataChannel { public: MockSctpDataChannel(int id, DataState state) : MockSctpDataChannel(id, @@ -41,11 +41,11 @@ class MockSctpDataChannel : public rtc::RefCountedObject { const InternalDataChannelInit& config = InternalDataChannelInit(), rtc::Thread* signaling_thread = rtc::Thread::Current(), rtc::Thread* network_thread = rtc::Thread::Current()) - : rtc::RefCountedObject(config, - nullptr, - label, - signaling_thread, - network_thread) { + : SctpDataChannel(config, + nullptr, + label, + signaling_thread, + network_thread) { EXPECT_CALL(*this, id()).WillRepeatedly(::testing::Return(id)); EXPECT_CALL(*this, state()).WillRepeatedly(::testing::Return(state)); EXPECT_CALL(*this, protocol()).WillRepeatedly(::testing::Return(protocol)); diff --git a/pc/test/mock_peer_connection_internal.h b/pc/test/mock_peer_connection_internal.h new file mode 100644 index 0000000000..4dd6db5f83 --- /dev/null +++ b/pc/test/mock_peer_connection_internal.h @@ -0,0 +1,325 @@ +/* + * Copyright 2022 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 PC_TEST_MOCK_PEER_CONNECTION_INTERNAL_H_ +#define PC_TEST_MOCK_PEER_CONNECTION_INTERNAL_H_ + +#include +#include +#include +#include +#include + +#include "pc/peer_connection_internal.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockPeerConnectionInternal : public PeerConnectionInternal { + public: + MockPeerConnectionInternal() {} + ~MockPeerConnectionInternal() = default; + // PeerConnectionInterface + MOCK_METHOD(rtc::scoped_refptr, + local_streams, + (), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + remote_streams, + (), + (override)); + MOCK_METHOD(bool, AddStream, (MediaStreamInterface*), (override)); + MOCK_METHOD(void, RemoveStream, (MediaStreamInterface*), (override)); + MOCK_METHOD(RTCErrorOr>, + AddTrack, + (rtc::scoped_refptr, + const std::vector&), + (override)); + MOCK_METHOD(RTCError, + RemoveTrackOrError, + (rtc::scoped_refptr), + (override)); + MOCK_METHOD(RTCErrorOr>, + AddTransceiver, + (rtc::scoped_refptr), + (override)); + MOCK_METHOD(RTCErrorOr>, + AddTransceiver, + (rtc::scoped_refptr, + const RtpTransceiverInit&), + (override)); + MOCK_METHOD(RTCErrorOr>, + AddTransceiver, + (cricket::MediaType), + (override)); + MOCK_METHOD(RTCErrorOr>, + AddTransceiver, + (cricket::MediaType, const RtpTransceiverInit&), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + CreateSender, + (const std::string&, const std::string&), + (override)); + MOCK_METHOD(std::vector>, + GetSenders, + (), + (const, override)); + MOCK_METHOD(std::vector>, + GetReceivers, + (), + (const, override)); + MOCK_METHOD(std::vector>, + GetTransceivers, + (), + (const, override)); + MOCK_METHOD(bool, + GetStats, + (StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel), + (override)); + MOCK_METHOD(void, GetStats, (RTCStatsCollectorCallback*), (override)); + MOCK_METHOD(void, + GetStats, + (rtc::scoped_refptr, + rtc::scoped_refptr), + (override)); + MOCK_METHOD(void, + GetStats, + (rtc::scoped_refptr, + rtc::scoped_refptr), + (override)); + MOCK_METHOD(void, ClearStatsCache, (), (override)); + MOCK_METHOD(RTCErrorOr>, + CreateDataChannelOrError, + (const std::string&, const DataChannelInit*), + (override)); + MOCK_METHOD(SessionDescriptionInterface*, + local_description, + (), + (const, override)); + MOCK_METHOD(SessionDescriptionInterface*, + remote_description, + (), + (const, override)); + MOCK_METHOD(SessionDescriptionInterface*, + current_local_description, + (), + (const, override)); + MOCK_METHOD(SessionDescriptionInterface*, + current_remote_description, + (), + (const, override)); + MOCK_METHOD(SessionDescriptionInterface*, + pending_local_description, + (), + (const, override)); + MOCK_METHOD(SessionDescriptionInterface*, + pending_remote_description, + (), + (const, override)); + MOCK_METHOD(void, RestartIce, (), (override)); + MOCK_METHOD(void, + CreateOffer, + (CreateSessionDescriptionObserver*, const RTCOfferAnswerOptions&), + (override)); + MOCK_METHOD(void, + CreateAnswer, + (CreateSessionDescriptionObserver*, const RTCOfferAnswerOptions&), + (override)); + + MOCK_METHOD(void, + SetLocalDescription, + (SetSessionDescriptionObserver*, SessionDescriptionInterface*), + (override)); + MOCK_METHOD(void, + SetRemoteDescription, + (SetSessionDescriptionObserver*, SessionDescriptionInterface*), + (override)); + MOCK_METHOD(void, + SetRemoteDescription, + (std::unique_ptr, + rtc::scoped_refptr), + (override)); + MOCK_METHOD(PeerConnectionInterface::RTCConfiguration, + GetConfiguration, + (), + (override)); + MOCK_METHOD(RTCError, + SetConfiguration, + (const PeerConnectionInterface::RTCConfiguration&), + (override)); + MOCK_METHOD(bool, + AddIceCandidate, + (const IceCandidateInterface*), + (override)); + MOCK_METHOD(bool, + RemoveIceCandidates, + (const std::vector&), + (override)); + MOCK_METHOD(RTCError, SetBitrate, (const BitrateSettings&), (override)); + MOCK_METHOD(void, SetAudioPlayout, (bool), (override)); + MOCK_METHOD(void, SetAudioRecording, (bool), (override)); + MOCK_METHOD(rtc::scoped_refptr, + LookupDtlsTransportByMid, + (const std::string&), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + GetSctpTransport, + (), + (const, override)); + MOCK_METHOD(SignalingState, signaling_state, (), (override)); + MOCK_METHOD(IceConnectionState, ice_connection_state, (), (override)); + MOCK_METHOD(IceConnectionState, + standardized_ice_connection_state, + (), + (override)); + MOCK_METHOD(PeerConnectionState, peer_connection_state, (), (override)); + MOCK_METHOD(IceGatheringState, ice_gathering_state, (), (override)); + MOCK_METHOD(absl::optional, can_trickle_ice_candidates, (), (override)); + MOCK_METHOD(bool, + StartRtcEventLog, + (std::unique_ptr, int64_t), + (override)); + MOCK_METHOD(bool, + StartRtcEventLog, + (std::unique_ptr), + (override)); + MOCK_METHOD(void, StopRtcEventLog, (), (override)); + MOCK_METHOD(void, Close, (), (override)); + MOCK_METHOD(rtc::Thread*, signaling_thread, (), (const, override)); + + // PeerConnectionSdpMethods + MOCK_METHOD(std::string, session_id, (), (const, override)); + MOCK_METHOD(bool, NeedsIceRestart, (const std::string&), (const, override)); + MOCK_METHOD(absl::optional, sctp_mid, (), (const, override)); + MOCK_METHOD(PeerConnectionInterface::RTCConfiguration*, + configuration, + (), + (const, override)); + MOCK_METHOD(void, + ReportSdpFormatReceived, + (const SessionDescriptionInterface&), + (override)); + MOCK_METHOD(void, + ReportSdpBundleUsage, + (const SessionDescriptionInterface&), + (override)); + MOCK_METHOD(PeerConnectionMessageHandler*, message_handler, (), (override)); + MOCK_METHOD(RtpTransmissionManager*, rtp_manager, (), (override)); + MOCK_METHOD(const RtpTransmissionManager*, + rtp_manager, + (), + (const, override)); + MOCK_METHOD(bool, dtls_enabled, (), (const, override)); + MOCK_METHOD(const PeerConnectionFactoryInterface::Options*, + options, + (), + (const, override)); + MOCK_METHOD(CryptoOptions, GetCryptoOptions, (), (override)); + MOCK_METHOD(JsepTransportController*, transport_controller_s, (), (override)); + MOCK_METHOD(JsepTransportController*, transport_controller_n, (), (override)); + MOCK_METHOD(DataChannelController*, data_channel_controller, (), (override)); + MOCK_METHOD(cricket::PortAllocator*, port_allocator, (), (override)); + MOCK_METHOD(StatsCollector*, stats, (), (override)); + MOCK_METHOD(PeerConnectionObserver*, Observer, (), (const, override)); + MOCK_METHOD(bool, GetSctpSslRole, (rtc::SSLRole*), (override)); + MOCK_METHOD(PeerConnectionInterface::IceConnectionState, + ice_connection_state_internal, + (), + (override)); + MOCK_METHOD(void, + SetIceConnectionState, + (PeerConnectionInterface::IceConnectionState), + (override)); + MOCK_METHOD(void, NoteUsageEvent, (UsageEvent), (override)); + MOCK_METHOD(bool, IsClosed, (), (const, override)); + MOCK_METHOD(bool, IsUnifiedPlan, (), (const, override)); + MOCK_METHOD(bool, + ValidateBundleSettings, + (const cricket::SessionDescription*, + (const std::map&)), + (override)); + MOCK_METHOD(absl::optional, GetDataMid, (), (const, override)); + MOCK_METHOD(RTCErrorOr>, + AddTransceiver, + (cricket::MediaType, + rtc::scoped_refptr, + const RtpTransceiverInit&, + bool), + (override)); + MOCK_METHOD(void, StartSctpTransport, (int, int, int), (override)); + MOCK_METHOD(void, + AddRemoteCandidate, + (const std::string&, const cricket::Candidate&), + (override)); + MOCK_METHOD(Call*, call_ptr, (), (override)); + MOCK_METHOD(bool, SrtpRequired, (), (const, override)); + MOCK_METHOD(bool, + SetupDataChannelTransport_n, + (const std::string&), + (override)); + MOCK_METHOD(void, TeardownDataChannelTransport_n, (), (override)); + MOCK_METHOD(void, SetSctpDataMid, (const std::string&), (override)); + MOCK_METHOD(void, ResetSctpDataMid, (), (override)); + MOCK_METHOD(const FieldTrialsView&, trials, (), (const, override)); + + // PeerConnectionInternal + MOCK_METHOD(rtc::Thread*, network_thread, (), (const, override)); + MOCK_METHOD(rtc::Thread*, worker_thread, (), (const, override)); + MOCK_METHOD(bool, initial_offerer, (), (const, override)); + MOCK_METHOD( + std::vector< + rtc::scoped_refptr>>, + GetTransceiversInternal, + (), + (const, override)); + MOCK_METHOD(sigslot::signal1&, + SignalSctpDataChannelCreated, + (), + (override)); + MOCK_METHOD(std::vector, + GetDataChannelStats, + (), + (const, override)); + MOCK_METHOD(absl::optional, + sctp_transport_name, + (), + (const, override)); + MOCK_METHOD(cricket::CandidateStatsList, + GetPooledCandidateStats, + (), + (const, override)); + MOCK_METHOD((std::map), + GetTransportStatsByNames, + (const std::set&), + (override)); + MOCK_METHOD(Call::Stats, GetCallStats, (), (override)); + MOCK_METHOD(bool, + GetLocalCertificate, + (const std::string&, rtc::scoped_refptr*), + (override)); + MOCK_METHOD(std::unique_ptr, + GetRemoteSSLCertChain, + (const std::string&), + (override)); + MOCK_METHOD(bool, IceRestartPending, (const std::string&), (const, override)); + MOCK_METHOD(bool, + GetSslRole, + (const std::string&, rtc::SSLRole*), + (override)); + MOCK_METHOD(void, NoteDataAddedEvent, (), (override)); + MOCK_METHOD(void, + OnSctpDataChannelClosed, + (DataChannelInterface*), + (override)); +}; + +} // namespace webrtc + +#endif // PC_TEST_MOCK_PEER_CONNECTION_INTERNAL_H_ diff --git a/pc/test/mock_peer_connection_observers.h b/pc/test/mock_peer_connection_observers.h index 2698d956af..e9d97a97f6 100644 --- a/pc/test/mock_peer_connection_observers.h +++ b/pc/test/mock_peer_connection_observers.h @@ -74,7 +74,9 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { MediaStreamInterface* RemoteStream(const std::string& label) { return remote_streams_->find(label); } - StreamCollectionInterface* remote_streams() const { return remote_streams_; } + StreamCollectionInterface* remote_streams() const { + return remote_streams_.get(); + } void OnAddStream(rtc::scoped_refptr stream) override { last_added_stream_ = stream; remote_streams_->AddStream(stream); @@ -82,7 +84,7 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { void OnRemoveStream( rtc::scoped_refptr stream) override { last_removed_stream_ = stream; - remote_streams_->RemoveStream(stream); + remote_streams_->RemoveStream(stream.get()); } void OnRenegotiationNeeded() override { renegotiation_needed_ = true; } void OnNegotiationNeededEvent(uint32_t event_id) override { @@ -336,7 +338,7 @@ class MockSetSessionDescriptionObserver }; class FakeSetLocalDescriptionObserver - : public rtc::RefCountedObject { + : public SetLocalDescriptionObserverInterface { public: bool called() const { return error_.has_value(); } RTCError& error() { @@ -355,7 +357,7 @@ class FakeSetLocalDescriptionObserver }; class FakeSetRemoteDescriptionObserver - : public rtc::RefCountedObject { + : public SetRemoteDescriptionObserverInterface { public: bool called() const { return error_.has_value(); } RTCError& error() { diff --git a/pc/test/mock_rtp_receiver_internal.h b/pc/test/mock_rtp_receiver_internal.h index ba244039af..779dcdcf08 100644 --- a/pc/test/mock_rtp_receiver_internal.h +++ b/pc/test/mock_rtp_receiver_internal.h @@ -57,7 +57,6 @@ class MockRtpReceiverInternal : public RtpReceiverInternal { // RtpReceiverInternal methods. MOCK_METHOD(void, Stop, (), (override)); - MOCK_METHOD(void, StopAndEndTrack, (), (override)); MOCK_METHOD(void, SetMediaChannel, (cricket::MediaChannel*), (override)); MOCK_METHOD(void, SetupMediaChannel, (uint32_t), (override)); MOCK_METHOD(void, SetupUnsignaledMediaChannel, (), (override)); diff --git a/pc/test/peer_connection_test_wrapper.cc b/pc/test/peer_connection_test_wrapper.cc index fef2cfb37a..fb120cd7aa 100644 --- a/pc/test/peer_connection_test_wrapper.cc +++ b/pc/test/peer_connection_test_wrapper.cc @@ -106,7 +106,7 @@ bool PeerConnectionTestWrapper::CreatePc( RTC_DCHECK_RUN_ON(&pc_thread_checker_); fake_audio_capture_module_ = FakeAudioCaptureModule::Create(); - if (fake_audio_capture_module_ == NULL) { + if (fake_audio_capture_module_ == nullptr) { return false; } @@ -184,7 +184,7 @@ void PeerConnectionTestWrapper::OnIceCandidate( void PeerConnectionTestWrapper::OnDataChannel( rtc::scoped_refptr data_channel) { - SignalOnDataChannel(data_channel); + SignalOnDataChannel(data_channel.get()); } void PeerConnectionTestWrapper::OnSuccess(SessionDescriptionInterface* desc) { @@ -237,7 +237,7 @@ void PeerConnectionTestWrapper::SetLocalDescription(SdpType type, auto observer = rtc::make_ref_counted(); peer_connection_->SetLocalDescription( - observer, webrtc::CreateSessionDescription(type, sdp).release()); + observer.get(), webrtc::CreateSessionDescription(type, sdp).release()); } void PeerConnectionTestWrapper::SetRemoteDescription(SdpType type, @@ -248,7 +248,7 @@ void PeerConnectionTestWrapper::SetRemoteDescription(SdpType type, auto observer = rtc::make_ref_counted(); peer_connection_->SetRemoteDescription( - observer, webrtc::CreateSessionDescription(type, sdp).release()); + observer.get(), webrtc::CreateSessionDescription(type, sdp).release()); } void PeerConnectionTestWrapper::AddIceCandidate(const std::string& sdp_mid, @@ -333,7 +333,7 @@ PeerConnectionTestWrapper::GetUserMedia( peer_connection_factory_->CreateAudioSource(options); rtc::scoped_refptr audio_track( peer_connection_factory_->CreateAudioTrack(kAudioTrackLabelBase, - source)); + source.get())); stream->AddTrack(audio_track); } @@ -348,7 +348,8 @@ PeerConnectionTestWrapper::GetUserMedia( std::string videotrack_label = stream_id + kVideoTrackLabelBase; rtc::scoped_refptr video_track( - peer_connection_factory_->CreateVideoTrack(videotrack_label, source)); + peer_connection_factory_->CreateVideoTrack(videotrack_label, + source.get())); stream->AddTrack(video_track); } diff --git a/pc/track_media_info_map.cc b/pc/track_media_info_map.cc index e68f2f7a52..bf3ec0f2bc 100644 --- a/pc/track_media_info_map.cc +++ b/pc/track_media_info_map.cc @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include "api/media_types.h" @@ -52,7 +52,7 @@ void GetAudioAndVideoTrackBySsrc( RTC_DCHECK(remote_video_track_by_ssrc->empty()); for (const auto& rtp_sender : rtp_senders) { cricket::MediaType media_type = rtp_sender->media_type(); - MediaStreamTrackInterface* track = rtp_sender->track(); + MediaStreamTrackInterface* track = rtp_sender->track().get(); if (!track) { continue; } @@ -74,7 +74,7 @@ void GetAudioAndVideoTrackBySsrc( } for (const auto& rtp_receiver : rtp_receivers) { cricket::MediaType media_type = rtp_receiver->media_type(); - MediaStreamTrackInterface* track = rtp_receiver->track(); + MediaStreamTrackInterface* track = rtp_receiver->track().get(); RTC_DCHECK(track); RtpParameters params = rtp_receiver->GetParameters(); for (const RtpEncodingParameters& encoding : params.encodings) { @@ -126,10 +126,10 @@ TrackMediaInfoMap::TrackMediaInfoMap( &unsignaled_video_track); for (const auto& sender : rtp_senders) { - attachment_id_by_track_[sender->track()] = sender->AttachmentId(); + attachment_id_by_track_[sender->track().get()] = sender->AttachmentId(); } for (const auto& receiver : rtp_receivers) { - attachment_id_by_track_[receiver->track()] = receiver->AttachmentId(); + attachment_id_by_track_[receiver->track().get()] = receiver->AttachmentId(); } if (voice_media_info_) { diff --git a/pc/track_media_info_map_unittest.cc b/pc/track_media_info_map_unittest.cc index a58331f0df..e9428ca130 100644 --- a/pc/track_media_info_map_unittest.cc +++ b/pc/track_media_info_map_unittest.cc @@ -10,22 +10,28 @@ #include "pc/track_media_info_map.h" +#include + +#include #include -#include #include +#include #include #include -#include "api/rtp_sender_interface.h" +#include "api/media_types.h" +#include "api/rtp_parameters.h" #include "api/test/mock_video_track.h" -#include "api/transport/rtp/rtp_source.h" #include "media/base/media_channel.h" #include "pc/audio_track.h" #include "pc/test/fake_video_track_source.h" #include "pc/test/mock_rtp_receiver_internal.h" #include "pc/test/mock_rtp_sender_internal.h" #include "pc/video_track.h" -#include "rtc_base/ref_count.h" +#include "rtc_base/checks.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" +#include "test/gmock.h" #include "test/gtest.h" namespace webrtc { @@ -199,10 +205,10 @@ class TrackMediaInfoMapTest : public ::testing::Test { } // namespace TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithOneSsrc) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); - AddRtpReceiverWithSsrcs({2}, remote_audio_track_); - AddRtpSenderWithSsrcs({3}, local_video_track_); - AddRtpReceiverWithSsrcs({4}, remote_video_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); + AddRtpSenderWithSsrcs({3}, local_video_track_.get()); + AddRtpReceiverWithSsrcs({4}, remote_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio sender @@ -235,10 +241,10 @@ TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithOneSsrc) { } TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithMissingSsrc) { - AddRtpSenderWithSsrcs({}, local_audio_track_); - AddRtpSenderWithSsrcs({}, local_video_track_); - AddRtpReceiverWithSsrcs({}, remote_audio_track_); - AddRtpReceiverWithSsrcs({}, remote_video_track_); + AddRtpSenderWithSsrcs({}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({}, local_video_track_.get()); + AddRtpReceiverWithSsrcs({}, remote_audio_track_.get()); + AddRtpReceiverWithSsrcs({}, remote_video_track_.get()); CreateMap(); EXPECT_FALSE(map_->GetVoiceSenderInfos(*local_audio_track_)); @@ -249,10 +255,10 @@ TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithMissingSsrc) { TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithAudioAndVideoUseSameSsrc) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); - AddRtpReceiverWithSsrcs({2}, remote_audio_track_); - AddRtpSenderWithSsrcs({1}, local_video_track_); - AddRtpReceiverWithSsrcs({2}, remote_video_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); + AddRtpSenderWithSsrcs({1}, local_video_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio sender @@ -285,8 +291,8 @@ TEST_F(TrackMediaInfoMapTest, } TEST_F(TrackMediaInfoMapTest, SingleMultiSsrcSenderPerTrack) { - AddRtpSenderWithSsrcs({1, 2}, local_audio_track_); - AddRtpSenderWithSsrcs({3, 4}, local_video_track_); + AddRtpSenderWithSsrcs({1, 2}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({3, 4}, local_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio senders @@ -307,10 +313,10 @@ TEST_F(TrackMediaInfoMapTest, SingleMultiSsrcSenderPerTrack) { } TEST_F(TrackMediaInfoMapTest, MultipleOneSsrcSendersPerTrack) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); - AddRtpSenderWithSsrcs({2}, local_audio_track_); - AddRtpSenderWithSsrcs({3}, local_video_track_); - AddRtpSenderWithSsrcs({4}, local_video_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({2}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({3}, local_video_track_.get()); + AddRtpSenderWithSsrcs({4}, local_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio senders @@ -337,10 +343,10 @@ TEST_F(TrackMediaInfoMapTest, MultipleOneSsrcSendersPerTrack) { } TEST_F(TrackMediaInfoMapTest, MultipleMultiSsrcSendersPerTrack) { - AddRtpSenderWithSsrcs({1, 2}, local_audio_track_); - AddRtpSenderWithSsrcs({3, 4}, local_audio_track_); - AddRtpSenderWithSsrcs({5, 6}, local_video_track_); - AddRtpSenderWithSsrcs({7, 8}, local_video_track_); + AddRtpSenderWithSsrcs({1, 2}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({3, 4}, local_audio_track_.get()); + AddRtpSenderWithSsrcs({5, 6}, local_video_track_.get()); + AddRtpSenderWithSsrcs({7, 8}, local_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio senders @@ -368,10 +374,10 @@ TEST_F(TrackMediaInfoMapTest, MultipleMultiSsrcSendersPerTrack) { // SSRCs can be reused for send and receive in loopback. TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithSsrcNotUnique) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); - AddRtpReceiverWithSsrcs({1}, remote_audio_track_); - AddRtpSenderWithSsrcs({2}, local_video_track_); - AddRtpReceiverWithSsrcs({2}, remote_video_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); + AddRtpReceiverWithSsrcs({1}, remote_audio_track_.get()); + AddRtpSenderWithSsrcs({2}, local_video_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_video_track_.get()); CreateMap(); // Local audio track <-> RTP audio senders @@ -404,10 +410,10 @@ TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithSsrcNotUnique) { } TEST_F(TrackMediaInfoMapTest, SsrcLookupFunction) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); - AddRtpReceiverWithSsrcs({2}, remote_audio_track_); - AddRtpSenderWithSsrcs({3}, local_video_track_); - AddRtpReceiverWithSsrcs({4}, remote_video_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); + AddRtpSenderWithSsrcs({3}, local_video_track_.get()); + AddRtpReceiverWithSsrcs({4}, remote_video_track_.get()); CreateMap(); EXPECT_TRUE(map_->GetVoiceSenderInfoBySsrc(1)); EXPECT_TRUE(map_->GetVoiceReceiverInfoBySsrc(2)); @@ -418,11 +424,12 @@ TEST_F(TrackMediaInfoMapTest, SsrcLookupFunction) { } TEST_F(TrackMediaInfoMapTest, GetAttachmentIdByTrack) { - AddRtpSenderWithSsrcs({1}, local_audio_track_); + AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); CreateMap(); EXPECT_EQ(rtp_senders_[0]->AttachmentId(), - map_->GetAttachmentIdByTrack(local_audio_track_)); - EXPECT_EQ(absl::nullopt, map_->GetAttachmentIdByTrack(local_video_track_)); + map_->GetAttachmentIdByTrack(local_audio_track_.get())); + EXPECT_EQ(absl::nullopt, + map_->GetAttachmentIdByTrack(local_video_track_.get())); } // Death tests. @@ -436,18 +443,18 @@ class TrackMediaInfoMapDeathTest : public TrackMediaInfoMapTest { }; TEST_F(TrackMediaInfoMapDeathTest, MultipleOneSsrcReceiversPerTrack) { - AddRtpReceiverWithSsrcs({1}, remote_audio_track_); - AddRtpReceiverWithSsrcs({2}, remote_audio_track_); - AddRtpReceiverWithSsrcs({3}, remote_video_track_); - AddRtpReceiverWithSsrcs({4}, remote_video_track_); + AddRtpReceiverWithSsrcs({1}, remote_audio_track_.get()); + AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); + AddRtpReceiverWithSsrcs({3}, remote_video_track_.get()); + AddRtpReceiverWithSsrcs({4}, remote_video_track_.get()); EXPECT_DEATH(CreateMap(), ""); } TEST_F(TrackMediaInfoMapDeathTest, MultipleMultiSsrcReceiversPerTrack) { - AddRtpReceiverWithSsrcs({1, 2}, remote_audio_track_); - AddRtpReceiverWithSsrcs({3, 4}, remote_audio_track_); - AddRtpReceiverWithSsrcs({5, 6}, remote_video_track_); - AddRtpReceiverWithSsrcs({7, 8}, remote_video_track_); + AddRtpReceiverWithSsrcs({1, 2}, remote_audio_track_.get()); + AddRtpReceiverWithSsrcs({3, 4}, remote_audio_track_.get()); + AddRtpReceiverWithSsrcs({5, 6}, remote_video_track_.get()); + AddRtpReceiverWithSsrcs({7, 8}, remote_video_track_.get()); EXPECT_DEATH(CreateMap(), ""); } diff --git a/pc/transceiver_list.cc b/pc/transceiver_list.cc index 235c9af036..250dfbc9e2 100644 --- a/pc/transceiver_list.cc +++ b/pc/transceiver_list.cc @@ -10,6 +10,8 @@ #include "pc/transceiver_list.h" +#include + #include "rtc_base/checks.h" namespace webrtc { @@ -29,7 +31,7 @@ void TransceiverStableState::SetMSectionIfUnset( } } -void TransceiverStableState::SetRemoteStreamIdsIfUnset( +void TransceiverStableState::SetRemoteStreamIds( const std::vector& ids) { if (!remote_stream_ids_.has_value()) { remote_stream_ids_ = ids; diff --git a/pc/transceiver_list.h b/pc/transceiver_list.h index 568c9c7e7a..848ccc2c3b 100644 --- a/pc/transceiver_list.h +++ b/pc/transceiver_list.h @@ -43,9 +43,13 @@ class TransceiverStableState { void set_newly_created(); void SetMSectionIfUnset(absl::optional mid, absl::optional mline_index); - void SetRemoteStreamIdsIfUnset(const std::vector& ids); + void SetRemoteStreamIds(const std::vector& ids); void SetInitSendEncodings( const std::vector& encodings); + void SetFiredDirection( + absl::optional fired_direction) { + fired_direction_ = fired_direction; + } absl::optional mid() const { return mid_; } absl::optional mline_index() const { return mline_index_; } absl::optional> remote_stream_ids() const { @@ -57,6 +61,13 @@ class TransceiverStableState { } bool has_m_section() const { return has_m_section_; } bool newly_created() const { return newly_created_; } + bool did_set_fired_direction() const { return fired_direction_.has_value(); } + // Because fired_direction() is nullable, did_set_fired_direction() is used to + // distinguish beteen "no value" and "null value". + absl::optional fired_direction() const { + RTC_DCHECK(did_set_fired_direction()); + return fired_direction_.value(); + } private: absl::optional mid_; @@ -71,6 +82,9 @@ class TransceiverStableState { // description to track potential need for removing transceiver during // rollback. bool newly_created_ = false; + // `fired_direction_` is nullable, so an optional of an optional is used to + // distinguish between null and not set (sorry if this hurts your eyes). + absl::optional> fired_direction_; }; // This class encapsulates the active list of transceivers on a diff --git a/pc/transport_stats.h b/pc/transport_stats.h index 2f43d45808..e554385954 100644 --- a/pc/transport_stats.h +++ b/pc/transport_stats.h @@ -31,6 +31,7 @@ struct TransportChannelStats { int ssl_version_bytes = 0; int srtp_crypto_suite = rtc::kSrtpInvalidCryptoSuite; int ssl_cipher_suite = rtc::kTlsNullWithNullNull; + absl::optional dtls_role; webrtc::DtlsTransportState dtls_state = webrtc::DtlsTransportState::kNew; IceTransportStats ice_transport_stats; }; diff --git a/pc/used_ids_unittest.cc b/pc/used_ids_unittest.cc index af66898450..6362f2773a 100644 --- a/pc/used_ids_unittest.cc +++ b/pc/used_ids_unittest.cc @@ -10,6 +10,7 @@ #include "pc/used_ids.h" +#include "absl/strings/string_view.h" #include "test/gtest.h" using cricket::UsedIds; diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc index d1f8fb0d96..3cc568f12a 100644 --- a/pc/video_rtp_receiver.cc +++ b/pc/video_rtp_receiver.cc @@ -12,15 +12,16 @@ #include +#include #include #include #include "api/video/recordable_encoded_frame.h" -#include "api/video_track_source_proxy_factory.h" #include "pc/video_track.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -109,74 +110,61 @@ void VideoRtpReceiver::SetDepacketizerToDecoderFrameTransformer( void VideoRtpReceiver::Stop() { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - // TODO(deadbeef): Need to do more here to fully stop receiving packets. - source_->SetState(MediaSourceInterface::kEnded); - - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - if (media_channel_) { - SetSink(nullptr); - SetMediaChannel_w(nullptr); - } - source_->ClearCallback(); - }); -} - -void VideoRtpReceiver::StopAndEndTrack() { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - Stop(); track_->internal()->set_ended(); } +// RTC_RUN_ON(&signaling_thread_checker_) void VideoRtpReceiver::RestartMediaChannel(absl::optional ssrc) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); - MediaSourceInterface::SourceState state = source_->state(); - // TODO(tommi): Can we restart the media channel without blocking? worker_thread_->Invoke(RTC_FROM_HERE, [&] { RTC_DCHECK_RUN_ON(worker_thread_); - if (!media_channel_) { - // Ignore further negotiations if we've already been stopped and don't - // have an associated media channel. - return; // Can't restart. - } + RestartMediaChannel_w(std::move(ssrc), state); + }); + source_->SetState(MediaSourceInterface::kLive); +} - const bool encoded_sink_enabled = saved_encoded_sink_enabled_; +// RTC_RUN_ON(worker_thread_) +void VideoRtpReceiver::RestartMediaChannel_w( + absl::optional ssrc, + MediaSourceInterface::SourceState state) { + if (!media_channel_) { + return; // Can't restart. + } - if (state != MediaSourceInterface::kInitializing) { - if (ssrc == ssrc_) - return; + const bool encoded_sink_enabled = saved_encoded_sink_enabled_; - // Disconnect from a previous ssrc. - SetSink(nullptr); + if (state != MediaSourceInterface::kInitializing) { + if (ssrc == ssrc_) + return; - if (encoded_sink_enabled) - SetEncodedSinkEnabled(false); - } + // Disconnect from a previous ssrc. + SetSink(nullptr); - // Set up the new ssrc. - ssrc_ = std::move(ssrc); - SetSink(source_->sink()); - if (encoded_sink_enabled) { - SetEncodedSinkEnabled(true); - } + if (encoded_sink_enabled) + SetEncodedSinkEnabled(false); + } - if (frame_transformer_ && media_channel_) { - media_channel_->SetDepacketizerToDecoderFrameTransformer( - ssrc_.value_or(0), frame_transformer_); - } + // Set up the new ssrc. + ssrc_ = std::move(ssrc); + SetSink(source_->sink()); + if (encoded_sink_enabled) { + SetEncodedSinkEnabled(true); + } - if (media_channel_ && ssrc_) { - if (frame_decryptor_) { - media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_); - } + if (frame_transformer_ && media_channel_) { + media_channel_->SetDepacketizerToDecoderFrameTransformer( + ssrc_.value_or(0), frame_transformer_); + } - media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); + if (media_channel_ && ssrc_) { + if (frame_decryptor_) { + media_channel_->SetFrameDecryptor(*ssrc_, frame_decryptor_); } - }); - source_->SetState(MediaSourceInterface::kLive); + + media_channel_->SetBaseMinimumPlayoutDelayMs(*ssrc_, delay_.GetMs()); + } } // RTC_RUN_ON(worker_thread_) @@ -228,7 +216,7 @@ void VideoRtpReceiver::SetStreams( } } if (removed) { - existing_stream->RemoveTrack(track_); + existing_stream->RemoveTrack(video_track()); } } // Add remote track to any streams that are new. @@ -242,7 +230,7 @@ void VideoRtpReceiver::SetStreams( } } if (added) { - stream->AddTrack(track_); + stream->AddTrack(video_track()); } } streams_ = streams; @@ -266,14 +254,11 @@ void VideoRtpReceiver::SetJitterBufferMinimumDelay( } void VideoRtpReceiver::SetMediaChannel(cricket::MediaChannel* media_channel) { - RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK_RUN_ON(worker_thread_); RTC_DCHECK(media_channel == nullptr || media_channel->media_type() == media_type()); - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTC_DCHECK_RUN_ON(worker_thread_); - SetMediaChannel_w(media_channel); - }); + SetMediaChannel_w(media_channel); } // RTC_RUN_ON(worker_thread_) @@ -281,6 +266,10 @@ void VideoRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { if (media_channel == media_channel_) return; + if (!media_channel) { + SetSink(nullptr); + } + bool encoded_sink_enabled = saved_encoded_sink_enabled_; if (encoded_sink_enabled && media_channel_) { // Turn off the old sink, if any. @@ -303,6 +292,9 @@ void VideoRtpReceiver::SetMediaChannel_w(cricket::MediaChannel* media_channel) { ssrc_.value_or(0), frame_transformer_); } } + + if (!media_channel) + source_->ClearCallback(); } void VideoRtpReceiver::NotifyFirstPacketReceived() { @@ -320,6 +312,19 @@ std::vector VideoRtpReceiver::GetSources() const { return media_channel_->GetSources(*ssrc_); } +void VideoRtpReceiver::SetupMediaChannel(absl::optional ssrc, + cricket::MediaChannel* media_channel) { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + RTC_DCHECK(media_channel); + MediaSourceInterface::SourceState state = source_->state(); + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + RTC_DCHECK_RUN_ON(worker_thread_); + SetMediaChannel_w(media_channel); + RestartMediaChannel_w(std::move(ssrc), state); + }); + source_->SetState(MediaSourceInterface::kLive); +} + void VideoRtpReceiver::OnGenerateKeyFrame() { RTC_DCHECK_RUN_ON(worker_thread_); if (!media_channel_) { diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h index 681f423e29..05532a2eb1 100644 --- a/pc/video_rtp_receiver.h +++ b/pc/video_rtp_receiver.h @@ -88,7 +88,6 @@ class VideoRtpReceiver : public RtpReceiverInternal { // RtpReceiverInternal implementation. void Stop() override; - void StopAndEndTrack() override; void SetupMediaChannel(uint32_t ssrc) override; void SetupUnsignaledMediaChannel() override; uint32_t ssrc() const override; @@ -110,8 +109,17 @@ class VideoRtpReceiver : public RtpReceiverInternal { std::vector GetSources() const override; + // Combines SetMediaChannel, SetupMediaChannel and + // SetupUnsignaledMediaChannel. + void SetupMediaChannel(absl::optional ssrc, + cricket::MediaChannel* media_channel); + private: - void RestartMediaChannel(absl::optional ssrc); + void RestartMediaChannel(absl::optional ssrc) + RTC_RUN_ON(&signaling_thread_checker_); + void RestartMediaChannel_w(absl::optional ssrc, + MediaSourceInterface::SourceState state) + RTC_RUN_ON(worker_thread_); void SetSink(rtc::VideoSinkInterface* sink) RTC_RUN_ON(worker_thread_); void SetMediaChannel_w(cricket::MediaChannel* media_channel) diff --git a/pc/video_rtp_receiver_unittest.cc b/pc/video_rtp_receiver_unittest.cc index 42ff2611ef..c13214fcbb 100644 --- a/pc/video_rtp_receiver_unittest.cc +++ b/pc/video_rtp_receiver_unittest.cc @@ -10,11 +10,17 @@ #include "pc/video_rtp_receiver.h" +#include #include +#include "api/task_queue/task_queue_base.h" +#include "api/video/recordable_encoded_frame.h" #include "api/video/test/mock_recordable_encoded_frame.h" #include "media/base/fake_media_engine.h" +#include "rtc_base/location.h" +#include "rtc_base/ref_counted_object.h" #include "test/gmock.h" +#include "test/gtest.h" using ::testing::_; using ::testing::AnyNumber; @@ -60,13 +66,20 @@ class VideoRtpReceiverTest : public testing::Test { std::string("receiver"), std::vector({"stream"}))) { worker_thread_->Start(); - receiver_->SetMediaChannel(&channel_); + SetMediaChannel(&channel_); } ~VideoRtpReceiverTest() override { - // Clear expectations that tests may have set up before calling Stop(). + // Clear expectations that tests may have set up before calling + // SetMediaChannel(nullptr). Mock::VerifyAndClearExpectations(&channel_); receiver_->Stop(); + SetMediaChannel(nullptr); + } + + void SetMediaChannel(cricket::MediaChannel* media_channel) { + worker_thread_->Invoke( + RTC_FROM_HERE, [&]() { receiver_->SetMediaChannel(media_channel); }); } webrtc::VideoTrackSourceInterface* Source() { @@ -94,23 +107,24 @@ TEST_F(VideoRtpReceiverTest, MockVideoMediaChannel channel2(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel_, GenerateKeyFrame).Times(0); EXPECT_CALL(channel2, GenerateKeyFrame).Times(0); - receiver_->SetMediaChannel(&channel2); + SetMediaChannel(&channel2); Mock::VerifyAndClearExpectations(&channel2); // Generate a key frame. When we switch channel next time, we will have to // re-generate it as we don't know if it was eventually received + EXPECT_CALL(channel2, GenerateKeyFrame).Times(1); Source()->GenerateKeyFrame(); MockVideoMediaChannel channel3(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel3, GenerateKeyFrame); - receiver_->SetMediaChannel(&channel3); + SetMediaChannel(&channel3); // Switching to a new channel should now not cause calls to GenerateKeyFrame. StrictMock channel4(nullptr, cricket::VideoOptions()); - receiver_->SetMediaChannel(&channel4); + SetMediaChannel(&channel4); - // We must call Stop() here since the mock media channels live on the stack - // and `receiver_` still has a pointer to those objects. - receiver_->Stop(); + // We must call SetMediaChannel(nullptr) here since the mock media channels + // live on the stack and `receiver_` still has a pointer to those objects. + SetMediaChannel(nullptr); } TEST_F(VideoRtpReceiverTest, EnablesEncodedOutput) { @@ -135,7 +149,7 @@ TEST_F(VideoRtpReceiverTest, DisablesEnablesEncodedOutputOnChannelSwitch) { Source()->AddEncodedSink(&sink); MockVideoMediaChannel channel2(nullptr, cricket::VideoOptions()); EXPECT_CALL(channel2, SetRecordableEncodedFrameCallback); - receiver_->SetMediaChannel(&channel2); + SetMediaChannel(&channel2); Mock::VerifyAndClearExpectations(&channel2); // When clearing encoded frame buffer function, we need channel switches @@ -143,11 +157,11 @@ TEST_F(VideoRtpReceiverTest, DisablesEnablesEncodedOutputOnChannelSwitch) { EXPECT_CALL(channel2, ClearRecordableEncodedFrameCallback); Source()->RemoveEncodedSink(&sink); StrictMock channel3(nullptr, cricket::VideoOptions()); - receiver_->SetMediaChannel(&channel3); + SetMediaChannel(&channel3); - // We must call Stop() here since the mock media channels live on the stack - // and `receiver_` still has a pointer to those objects. - receiver_->Stop(); + // We must call SetMediaChannel(nullptr) here since the mock media channels + // live on the stack and `receiver_` still has a pointer to those objects. + SetMediaChannel(nullptr); } TEST_F(VideoRtpReceiverTest, BroadcastsEncodedFramesWhenEnabled) { diff --git a/pc/video_rtp_track_source_unittest.cc b/pc/video_rtp_track_source_unittest.cc index 5666b77d5f..bb1dc193de 100644 --- a/pc/video_rtp_track_source_unittest.cc +++ b/pc/video_rtp_track_source_unittest.cc @@ -10,6 +10,12 @@ #include "pc/video_rtp_track_source.h" +#include "absl/types/optional.h" +#include "api/scoped_refptr.h" +#include "api/units/timestamp.h" +#include "api/video/color_space.h" +#include "api/video/encoded_image.h" +#include "api/video/video_codec_type.h" #include "rtc_base/ref_counted_object.h" #include "test/gmock.h" #include "test/gtest.h" diff --git a/pc/video_track.cc b/pc/video_track.cc index 4559181ce7..95e27a3c96 100644 --- a/pc/video_track.cc +++ b/pc/video_track.cc @@ -10,7 +10,6 @@ #include "pc/video_track.h" -#include #include #include @@ -18,7 +17,6 @@ #include "api/sequence_checker.h" #include "rtc_base/checks.h" #include "rtc_base/location.h" -#include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" namespace webrtc { @@ -134,12 +132,12 @@ void VideoTrack::OnChanged() { rtc::scoped_refptr VideoTrack::Create( const std::string& id, - VideoTrackSourceInterface* source, + rtc::scoped_refptr source, rtc::Thread* worker_thread) { rtc::scoped_refptr< VideoTrackSourceProxyWithInternal> - source_proxy = VideoTrackSourceProxy::Create(rtc::Thread::Current(), - worker_thread, source); + source_proxy = VideoTrackSourceProxy::Create( + rtc::Thread::Current(), worker_thread, std::move(source)); return rtc::make_ref_counted(id, std::move(source_proxy), worker_thread); diff --git a/pc/video_track.h b/pc/video_track.h index 66262d22d1..8934e96592 100644 --- a/pc/video_track.h +++ b/pc/video_track.h @@ -13,6 +13,7 @@ #include +#include "absl/types/optional.h" #include "api/media_stream_interface.h" #include "api/media_stream_track.h" #include "api/scoped_refptr.h" @@ -21,8 +22,8 @@ #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" #include "media/base/video_source_base.h" -#include "rtc_base/system/no_unique_address.h" #include "pc/video_track_source_proxy.h" +#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -38,7 +39,7 @@ class VideoTrack : public MediaStreamTrack, public: static rtc::scoped_refptr Create( const std::string& label, - VideoTrackSourceInterface* source, + rtc::scoped_refptr source, rtc::Thread* worker_thread); void AddOrUpdateSink(rtc::VideoSinkInterface* sink, diff --git a/pc/video_track_source.h b/pc/video_track_source.h index 3f568f642b..723b10d8f3 100644 --- a/pc/video_track_source.h +++ b/pc/video_track_source.h @@ -20,8 +20,10 @@ #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" #include "media/base/media_channel.h" +#include "rtc_base/checks.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { diff --git a/pc/video_track_source_proxy.cc b/pc/video_track_source_proxy.cc index 309c1f20f8..c3e95e23cc 100644 --- a/pc/video_track_source_proxy.cc +++ b/pc/video_track_source_proxy.cc @@ -11,7 +11,9 @@ #include "pc/video_track_source_proxy.h" #include "api/media_stream_interface.h" +#include "api/scoped_refptr.h" #include "api/video_track_source_proxy_factory.h" +#include "rtc_base/thread.h" namespace webrtc { @@ -19,7 +21,9 @@ rtc::scoped_refptr CreateVideoTrackSourceProxy( rtc::Thread* signaling_thread, rtc::Thread* worker_thread, VideoTrackSourceInterface* source) { - return VideoTrackSourceProxy::Create(signaling_thread, worker_thread, source); + return VideoTrackSourceProxy::Create( + signaling_thread, worker_thread, + rtc::scoped_refptr(source)); } } // namespace webrtc diff --git a/pc/video_track_source_proxy.h b/pc/video_track_source_proxy.h index 1f6d976ba8..8500a98766 100644 --- a/pc/video_track_source_proxy.h +++ b/pc/video_track_source_proxy.h @@ -11,7 +11,13 @@ #ifndef PC_VIDEO_TRACK_SOURCE_PROXY_H_ #define PC_VIDEO_TRACK_SOURCE_PROXY_H_ +#include "absl/types/optional.h" #include "api/media_stream_interface.h" +#include "api/video/recordable_encoded_frame.h" +#include "api/video/video_frame.h" +#include "api/video/video_sink_interface.h" +#include "api/video/video_source_interface.h" +#include "api/video_track_source_constraints.h" #include "pc/proxy.h" namespace webrtc { @@ -21,6 +27,7 @@ namespace webrtc { // TODO(deadbeef): Move this to .cc file. What threads methods are called on is // an implementation detail. BEGIN_PROXY_MAP(VideoTrackSource) + PROXY_PRIMARY_THREAD_DESTRUCTOR() PROXY_CONSTMETHOD0(SourceState, state) BYPASS_PROXY_CONSTMETHOD0(bool, remote) diff --git a/pc/video_track_unittest.cc b/pc/video_track_unittest.cc index e6dcce7939..2a10c93f7b 100644 --- a/pc/video_track_unittest.cc +++ b/pc/video_track_unittest.cc @@ -13,11 +13,11 @@ #include #include "media/base/fake_frame_source.h" -#include "media/base/video_common.h" #include "pc/test/fake_video_track_renderer.h" #include "pc/test/fake_video_track_source.h" #include "pc/video_track_source.h" #include "rtc_base/ref_counted_object.h" +#include "rtc_base/time_utils.h" #include "test/gtest.h" using webrtc::FakeVideoTrackRenderer; diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index 3f06f307a4..4ca1636e54 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -12,9 +12,9 @@ #include #include -#include #include +#include #include #include #include @@ -25,12 +25,14 @@ #include #include "absl/algorithm/container.h" +#include "absl/strings/ascii.h" #include "api/candidate.h" #include "api/crypto_params.h" #include "api/jsep_ice_candidate.h" #include "api/jsep_session_description.h" #include "api/media_types.h" // for RtpExtension +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" @@ -152,8 +154,6 @@ static const char kMediaStreamSemantic[] = "WMS"; static const char kSsrcAttributeMsid[] = "msid"; static const char kDefaultMsid[] = "default"; static const char kNoStreamMsid[] = "-"; -static const char kSsrcAttributeMslabel[] = "mslabel"; -static const char kSSrcAttributeLabel[] = "label"; static const char kAttributeSsrcGroup[] = "ssrc-group"; static const char kAttributeCrypto[] = "crypto"; static const char kAttributeCandidate[] = "candidate"; @@ -218,7 +218,6 @@ static const char kSdpDelimiterColonChar = ':'; static const char kSdpDelimiterSemicolon[] = ";"; static const char kSdpDelimiterSemicolonChar = ';'; static const char kSdpDelimiterSlashChar = '/'; -static const char kNewLine[] = "\n"; static const char kNewLineChar = '\n'; static const char kReturnChar = '\r'; static const char kLineBreak[] = "\r\n"; @@ -265,11 +264,6 @@ struct SsrcInfo { std::string cname; std::string stream_id; std::string track_id; - - // For backward compatibility. - // TODO(ronghuawu): Remove below 2 fields once all the clients support msid. - std::string label; - std::string mslabel; }; typedef std::vector SsrcInfoVec; typedef std::vector SsrcGroupVec; @@ -294,7 +288,7 @@ static void BuildCandidate(const std::vector& candidates, std::string* message); static void BuildIceOptions(const std::vector& transport_options, std::string* message); -static bool ParseSessionDescription(const std::string& message, +static bool ParseSessionDescription(absl::string_view message, size_t* pos, std::string* session_id, std::string* session_version, @@ -304,7 +298,7 @@ static bool ParseSessionDescription(const std::string& message, cricket::SessionDescription* desc, SdpParseError* error); static bool ParseMediaDescription( - const std::string& message, + absl::string_view message, const TransportDescription& session_td, const RtpHeaderExtensions& session_extmaps, size_t* pos, @@ -313,10 +307,10 @@ static bool ParseMediaDescription( std::vector>* candidates, SdpParseError* error); static bool ParseContent( - const std::string& message, + absl::string_view message, const cricket::MediaType media_type, int mline_index, - const std::string& protocol, + absl::string_view protocol, const std::vector& payload_types, size_t* pos, std::string* content_name, @@ -326,54 +320,54 @@ static bool ParseContent( TransportDescription* transport, std::vector>* candidates, SdpParseError* error); -static bool ParseGroupAttribute(const std::string& line, +static bool ParseGroupAttribute(absl::string_view line, cricket::SessionDescription* desc, SdpParseError* error); -static bool ParseSsrcAttribute(const std::string& line, +static bool ParseSsrcAttribute(absl::string_view line, SsrcInfoVec* ssrc_infos, int* msid_signaling, SdpParseError* error); -static bool ParseSsrcGroupAttribute(const std::string& line, +static bool ParseSsrcGroupAttribute(absl::string_view line, SsrcGroupVec* ssrc_groups, SdpParseError* error); -static bool ParseCryptoAttribute(const std::string& line, +static bool ParseCryptoAttribute(absl::string_view line, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseRtpmapAttribute(const std::string& line, +static bool ParseRtpmapAttribute(absl::string_view line, const cricket::MediaType media_type, const std::vector& payload_types, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseFmtpAttributes(const std::string& line, +static bool ParseFmtpAttributes(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseFmtpParam(const std::string& line, +static bool ParseFmtpParam(absl::string_view line, std::string* parameter, std::string* value, SdpParseError* error); -static bool ParsePacketizationAttribute(const std::string& line, +static bool ParsePacketizationAttribute(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseRtcpFbAttribute(const std::string& line, +static bool ParseRtcpFbAttribute(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error); -static bool ParseIceOptions(const std::string& line, +static bool ParseIceOptions(absl::string_view line, std::vector* transport_options, SdpParseError* error); -static bool ParseExtmap(const std::string& line, +static bool ParseExtmap(absl::string_view line, RtpExtension* extmap, SdpParseError* error); static bool ParseFingerprintAttribute( - const std::string& line, + absl::string_view line, std::unique_ptr* fingerprint, SdpParseError* error); -static bool ParseDtlsSetup(const std::string& line, +static bool ParseDtlsSetup(absl::string_view line, cricket::ConnectionRole* role, SdpParseError* error); -static bool ParseMsidAttribute(const std::string& line, +static bool ParseMsidAttribute(absl::string_view line, std::vector* stream_ids, std::string* track_id, SdpParseError* error); @@ -406,7 +400,7 @@ static bool ParseFailed(absl::string_view message, SdpParseError* error) { // Get the first line of `message` from `line_start`. absl::string_view first_line; - size_t line_end = message.find(kNewLine, line_start); + size_t line_end = message.find(kNewLineChar, line_start); if (line_end != std::string::npos) { if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) { --line_end; @@ -464,7 +458,7 @@ static bool ParseFailedExpectMinFieldNum(absl::string_view line, // `line` is the failing line. The failure is due to the fact that it failed to // get the value of `attribute`. static bool ParseFailedGetValue(absl::string_view line, - const std::string& attribute, + absl::string_view attribute, SdpParseError* error) { rtc::StringBuilder description; description << "Failed to get the value of attribute: " << attribute; @@ -486,30 +480,34 @@ static bool ParseFailedExpectLine(absl::string_view message, return ParseFailed(message, line_start, description.Release(), error); } -static bool AddLine(const std::string& line, std::string* message) { +static bool AddLine(absl::string_view line, std::string* message) { if (!message) return false; - message->append(line); + message->append(line.data(), line.size()); message->append(kLineBreak); return true; } -static bool GetLine(const std::string& message, - size_t* pos, - std::string* line) { - size_t line_begin = *pos; - size_t line_end = message.find(kNewLine, line_begin); - if (line_end == std::string::npos) { - return false; +// Trim return character, if any. +static absl::string_view TrimReturnChar(absl::string_view line) { + if (!line.empty() && line.back() == kReturnChar) { + line.remove_suffix(1); } - // Update the new start position - *pos = line_end + 1; - if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) { - --line_end; + return line; +} + +// Gets line of `message` starting at `pos`, and checks overall SDP syntax. On +// success, advances `pos` to the next line. +static absl::optional GetLine(absl::string_view message, + size_t* pos) { + size_t line_end = message.find(kNewLineChar, *pos); + if (line_end == absl::string_view::npos) { + return absl::nullopt; } - *line = message.substr(line_begin, (line_end - line_begin)); - const char* cline = line->c_str(); + absl::string_view line = + TrimReturnChar(message.substr(*pos, line_end - *pos)); + // RFC 4566 // An SDP session description consists of a number of lines of text of // the form: @@ -523,31 +521,30 @@ static bool GetLine(const std::string& message, // // If a session has no meaningful name, the value "s= " SHOULD be used // (i.e., a single space as the session name). - if (line->length() < 3 || !islower(cline[0]) || - cline[1] != kSdpDelimiterEqualChar || - (cline[0] != kLineTypeSessionName && - cline[2] == kSdpDelimiterSpaceChar)) { - *pos = line_begin; - return false; + if (line.length() < 3 || !islower(static_cast(line[0])) || + line[1] != kSdpDelimiterEqualChar || + (line[0] != kLineTypeSessionName && line[2] == kSdpDelimiterSpaceChar)) { + return absl::nullopt; } - return true; + *pos = line_end + 1; + return line; } // Init `os` to "`type`=`value`". static void InitLine(const char type, - const std::string& value, + absl::string_view value, rtc::StringBuilder* os) { os->Clear(); *os << std::string(1, type) << kSdpDelimiterEqual << value; } // Init `os` to "a=`attribute`". -static void InitAttrLine(const std::string& attribute, rtc::StringBuilder* os) { +static void InitAttrLine(absl::string_view attribute, rtc::StringBuilder* os) { InitLine(kLineTypeAttributes, attribute, os); } // Writes a SDP attribute line based on `attribute` and `value` to `message`. -static void AddAttributeLine(const std::string& attribute, +static void AddAttributeLine(absl::string_view attribute, int value, std::string* message) { rtc::StringBuilder os; @@ -556,37 +553,29 @@ static void AddAttributeLine(const std::string& attribute, AddLine(os.str(), message); } -static bool IsLineType(const std::string& message, +static bool IsLineType(absl::string_view message, const char type, size_t line_start) { if (message.size() < line_start + kLinePrefixLength) { return false; } - const char* cmessage = message.c_str(); - return (cmessage[line_start] == type && - cmessage[line_start + 1] == kSdpDelimiterEqualChar); + return (message[line_start] == type && + message[line_start + 1] == kSdpDelimiterEqualChar); } -static bool IsLineType(const std::string& line, const char type) { +static bool IsLineType(absl::string_view line, const char type) { return IsLineType(line, type, 0); } -static bool GetLineWithType(const std::string& message, - size_t* pos, - std::string* line, - const char type) { - if (!IsLineType(message, type, *pos)) { - return false; +static absl::optional +GetLineWithType(absl::string_view message, size_t* pos, const char type) { + if (IsLineType(message, type, *pos)) { + return GetLine(message, pos); } - - if (!GetLine(message, pos, line)) - return false; - - return true; + return absl::nullopt; } -static bool HasAttribute(const std::string& line, - const std::string& attribute) { +static bool HasAttribute(absl::string_view line, absl::string_view attribute) { if (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0) { // Make sure that the match is not only a partial match. If length of // strings doesn't match, the next character of the line must be ':' or ' '. @@ -603,8 +592,8 @@ static bool HasAttribute(const std::string& line, } static bool AddSsrcLine(uint32_t ssrc_id, - const std::string& attribute, - const std::string& value, + absl::string_view attribute, + absl::string_view value, std::string* message) { // RFC 5576 // a=ssrc: : @@ -616,8 +605,8 @@ static bool AddSsrcLine(uint32_t ssrc_id, } // Get value only from :. -static bool GetValue(const std::string& message, - const std::string& attribute, +static bool GetValue(absl::string_view message, + absl::string_view attribute, std::string* value, SdpParseError* error) { std::string leftpart; @@ -626,16 +615,17 @@ static bool GetValue(const std::string& message, } // The left part should end with the expected attribute. if (leftpart.length() < attribute.length() || - leftpart.compare(leftpart.length() - attribute.length(), - attribute.length(), attribute) != 0) { + absl::string_view(leftpart).compare( + leftpart.length() - attribute.length(), attribute.length(), + attribute) != 0) { return ParseFailedGetValue(message, attribute, error); } return true; } // Get a single [token] from : -static bool GetSingleTokenValue(const std::string& message, - const std::string& attribute, +static bool GetSingleTokenValue(absl::string_view message, + absl::string_view attribute, std::string* value, SdpParseError* error) { if (!GetValue(message, attribute, value, error)) { @@ -656,8 +646,8 @@ static bool CaseInsensitiveFind(std::string str1, std::string str2) { } template -static bool GetValueFromString(const std::string& line, - const std::string& s, +static bool GetValueFromString(absl::string_view line, + absl::string_view s, T* t, SdpParseError* error) { if (!rtc::FromString(s, t)) { @@ -668,8 +658,8 @@ static bool GetValueFromString(const std::string& line, return true; } -static bool GetPayloadTypeFromString(const std::string& line, - const std::string& s, +static bool GetPayloadTypeFromString(absl::string_view line, + absl::string_view s, int* payload_type, SdpParseError* error) { return GetValueFromString(line, s, payload_type, error) && @@ -680,7 +670,7 @@ static bool GetPayloadTypeFromString(const std::string& line, // This is a track that does not contain SSRCs and only contains // stream_ids/track_id if it's signaled with a=msid lines. void CreateTrackWithNoSsrcs(const std::vector& msid_stream_ids, - const std::string& msid_track_id, + absl::string_view msid_track_id, const std::vector& rids, StreamParamsVec* tracks) { StreamParams track; @@ -690,7 +680,7 @@ void CreateTrackWithNoSsrcs(const std::vector& msid_stream_ids, return; } track.set_stream_ids(msid_stream_ids); - track.id = msid_track_id; + track.id = std::string(msid_track_id); track.set_rids(rids); tracks->push_back(track); } @@ -701,7 +691,7 @@ void CreateTrackWithNoSsrcs(const std::vector& msid_stream_ids, // exist. We prioritize getting stream_ids/track_ids signaled in a=msid lines. void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos, const std::vector& msid_stream_ids, - const std::string& msid_track_id, + absl::string_view msid_track_id, StreamParamsVec* tracks, int msid_signaling) { RTC_DCHECK(tracks != NULL); @@ -717,17 +707,11 @@ void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos, if (msid_signaling & cricket::kMsidSignalingMediaSection) { // This is the case with Unified Plan SDP msid signaling. stream_ids = msid_stream_ids; - track_id = msid_track_id; + track_id = std::string(msid_track_id); } else if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) { // This is the case with Plan B SDP msid signaling. stream_ids.push_back(ssrc_info.stream_id); track_id = ssrc_info.track_id; - } else if (!ssrc_info.mslabel.empty()) { - // Since there's no a=msid or a=ssrc msid signaling, this is a sdp from - // an older version of client that doesn't support msid. - // In that case, we use the mslabel and label to construct the track. - stream_ids.push_back(ssrc_info.mslabel); - track_id = ssrc_info.label; } else { // Since no media streams isn't supported with older SDP signaling, we // use a default a stream id. @@ -776,7 +760,7 @@ static const int kPreferenceHost = 1; static const int kPreferenceReflexive = 2; static const int kPreferenceRelayed = 3; -static int GetCandidatePreferenceFromType(const std::string& type) { +static int GetCandidatePreferenceFromType(absl::string_view type) { int preference = kPreferenceUnknown; if (type == cricket::LOCAL_PORT_TYPE) { preference = kPreferenceHost; @@ -988,7 +972,7 @@ std::string SdpSerializeCandidate(const cricket::Candidate& candidate) { return message; } -bool SdpDeserialize(const std::string& message, +bool SdpDeserialize(absl::string_view message, JsepSessionDescription* jdesc, SdpParseError* error) { std::string session_id; @@ -1022,7 +1006,7 @@ bool SdpDeserialize(const std::string& message, return true; } -bool SdpDeserializeCandidate(const std::string& message, +bool SdpDeserializeCandidate(absl::string_view message, JsepIceCandidate* jcandidate, SdpParseError* error) { RTC_DCHECK(jcandidate != NULL); @@ -1034,8 +1018,8 @@ bool SdpDeserializeCandidate(const std::string& message, return true; } -bool SdpDeserializeCandidate(const std::string& transport_name, - const std::string& message, +bool SdpDeserializeCandidate(absl::string_view transport_name, + absl::string_view message, cricket::Candidate* candidate, SdpParseError* error) { RTC_DCHECK(candidate != nullptr); @@ -1046,26 +1030,27 @@ bool SdpDeserializeCandidate(const std::string& transport_name, return true; } -bool ParseCandidate(const std::string& message, +bool ParseCandidate(absl::string_view message, Candidate* candidate, SdpParseError* error, bool is_raw) { RTC_DCHECK(candidate != NULL); - // Get the first line from `message`. - std::string first_line = message; - size_t pos = 0; - GetLine(message, &pos, &first_line); - // Makes sure `message` contains only one line. - if (message.size() > first_line.size()) { - std::string left, right; - if (rtc::tokenize_first(message, kNewLineChar, &left, &right) && - !right.empty()) { - return ParseFailed(message, 0, "Expect one line only", error); - } + absl::string_view first_line; + + size_t line_end = message.find(kNewLineChar); + if (line_end == absl::string_view::npos) { + first_line = message; + } else if (line_end + 1 == message.size()) { + first_line = message.substr(0, line_end); + } else { + return ParseFailed(message, 0, "Expect one line only", error); } + // Trim return char, if any. + first_line = TrimReturnChar(first_line); + // From WebRTC draft section 4.8.1.1 candidate-attribute should be // candidate: when trickled, but we still support // a=candidate:CRLF for backward compatibility and for parsing a line @@ -1248,7 +1233,7 @@ bool ParseCandidate(const std::string& message, return true; } -bool ParseIceOptions(const std::string& line, +bool ParseIceOptions(absl::string_view line, std::vector* transport_options, SdpParseError* error) { std::string ice_options; @@ -1263,7 +1248,7 @@ bool ParseIceOptions(const std::string& line, return true; } -bool ParseSctpPort(const std::string& line, +bool ParseSctpPort(absl::string_view line, int* sctp_port, SdpParseError* error) { // draft-ietf-mmusic-sctp-sdp-26 @@ -1284,7 +1269,7 @@ bool ParseSctpPort(const std::string& line, return true; } -bool ParseSctpMaxMessageSize(const std::string& line, +bool ParseSctpMaxMessageSize(absl::string_view line, int* max_message_size, SdpParseError* error) { // draft-ietf-mmusic-sctp-sdp-26 @@ -1301,7 +1286,7 @@ bool ParseSctpMaxMessageSize(const std::string& line, return true; } -bool ParseExtmap(const std::string& line, +bool ParseExtmap(absl::string_view line, RtpExtension* extmap, SdpParseError* error) { // RFC 5285 @@ -1738,15 +1723,6 @@ void BuildRtpContentAttributes(const MediaContentDescription* media_desc, << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track.id; AddLine(os.str(), message); - - // TODO(ronghuawu): Remove below code which is for backward - // compatibility. - // draft-alvestrand-rtcweb-mid-01 - // a=ssrc: mslabel: - // The label isn't yet defined. - // a=ssrc: label: - AddSsrcLine(ssrc, kSsrcAttributeMslabel, stream_id, message); - AddSsrcLine(ssrc, kSSrcAttributeLabel, track.id, message); } } @@ -1807,10 +1783,10 @@ void WriteRtcpFbHeader(int payload_type, rtc::StringBuilder* os) { } } -void WriteFmtpParameter(const std::string& parameter_name, - const std::string& parameter_value, +void WriteFmtpParameter(absl::string_view parameter_name, + absl::string_view parameter_value, rtc::StringBuilder* os) { - if (parameter_name == "") { + if (parameter_name.empty()) { // RFC 2198 and RFC 4733 don't use key-value pairs. *os << parameter_value; } else { @@ -1819,7 +1795,7 @@ void WriteFmtpParameter(const std::string& parameter_name, } } -bool IsFmtpParam(const std::string& name) { +bool IsFmtpParam(absl::string_view name) { // RFC 4855, section 3 specifies the mapping of media format parameters to SDP // parameters. Only ptime, maxptime, channels and rate are placed outside of // the fmtp line. In WebRTC, channels and rate are already handled separately @@ -2060,7 +2036,7 @@ void BuildIceOptions(const std::vector& transport_options, } } -bool ParseConnectionData(const std::string& line, +bool ParseConnectionData(absl::string_view line, rtc::SocketAddress* addr, SdpParseError* error) { // Parse the line from left to right. @@ -2111,7 +2087,7 @@ bool ParseConnectionData(const std::string& line, return true; } -bool ParseSessionDescription(const std::string& message, +bool ParseSessionDescription(absl::string_view message, size_t* pos, std::string* session_id, std::string* session_version, @@ -2120,69 +2096,74 @@ bool ParseSessionDescription(const std::string& message, rtc::SocketAddress* connection_addr, cricket::SessionDescription* desc, SdpParseError* error) { - std::string line; + absl::optional line; desc->set_msid_supported(false); desc->set_extmap_allow_mixed(false); // RFC 4566 // v= (protocol version) - if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) { + line = GetLineWithType(message, pos, kLineTypeVersion); + if (!line) { return ParseFailedExpectLine(message, *pos, kLineTypeVersion, std::string(), error); } // RFC 4566 // o= // - if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) { + line = GetLineWithType(message, pos, kLineTypeOrigin); + if (!line) { return ParseFailedExpectLine(message, *pos, kLineTypeOrigin, std::string(), error); } std::vector fields; - rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields); + rtc::split(line->substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields); const size_t expected_fields = 6; if (fields.size() != expected_fields) { - return ParseFailedExpectFieldNum(line, expected_fields, error); + return ParseFailedExpectFieldNum(*line, expected_fields, error); } *session_id = fields[1]; *session_version = fields[2]; // RFC 4566 // s= (session name) - if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) { + line = GetLineWithType(message, pos, kLineTypeSessionName); + if (!line) { return ParseFailedExpectLine(message, *pos, kLineTypeSessionName, std::string(), error); } - // absl::optional lines + // optional lines // Those are the optional lines, so shouldn't return false if not present. // RFC 4566 // i=* (session information) - GetLineWithType(message, pos, &line, kLineTypeSessionInfo); + GetLineWithType(message, pos, kLineTypeSessionInfo); // RFC 4566 // u=* (URI of description) - GetLineWithType(message, pos, &line, kLineTypeSessionUri); + GetLineWithType(message, pos, kLineTypeSessionUri); // RFC 4566 // e=* (email address) - GetLineWithType(message, pos, &line, kLineTypeSessionEmail); + GetLineWithType(message, pos, kLineTypeSessionEmail); // RFC 4566 // p=* (phone number) - GetLineWithType(message, pos, &line, kLineTypeSessionPhone); + GetLineWithType(message, pos, kLineTypeSessionPhone); // RFC 4566 // c=* (connection information -- not required if included in // all media) - if (GetLineWithType(message, pos, &line, kLineTypeConnection)) { - if (!ParseConnectionData(line, connection_addr, error)) { + if (absl::optional cline = + GetLineWithType(message, pos, kLineTypeConnection); + cline.has_value()) { + if (!ParseConnectionData(*cline, connection_addr, error)) { return false; } } // RFC 4566 // b=* (zero or more bandwidth information lines) - while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) { + while (GetLineWithType(message, pos, kLineTypeSessionBandwidth).has_value()) { // By pass zero or more b lines. } @@ -2191,80 +2172,81 @@ bool ParseSessionDescription(const std::string& message, // t= (time the session is active) // r=* (zero or more repeat times) // Ensure there's at least one time description - if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) { + if (!GetLineWithType(message, pos, kLineTypeTiming).has_value()) { return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(), error); } - while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) { + while (GetLineWithType(message, pos, kLineTypeRepeatTimes).has_value()) { // By pass zero or more r lines. } // Go through the rest of the time descriptions - while (GetLineWithType(message, pos, &line, kLineTypeTiming)) { - while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) { + while (GetLineWithType(message, pos, kLineTypeTiming).has_value()) { + while (GetLineWithType(message, pos, kLineTypeRepeatTimes).has_value()) { // By pass zero or more r lines. } } // RFC 4566 // z=* (time zone adjustments) - GetLineWithType(message, pos, &line, kLineTypeTimeZone); + GetLineWithType(message, pos, kLineTypeTimeZone); // RFC 4566 // k=* (encryption key) - GetLineWithType(message, pos, &line, kLineTypeEncryptionKey); + GetLineWithType(message, pos, kLineTypeEncryptionKey); // RFC 4566 // a=* (zero or more session attribute lines) - while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) { - if (HasAttribute(line, kAttributeGroup)) { - if (!ParseGroupAttribute(line, desc, error)) { + while (absl::optional aline = + GetLineWithType(message, pos, kLineTypeAttributes)) { + if (HasAttribute(*aline, kAttributeGroup)) { + if (!ParseGroupAttribute(*aline, desc, error)) { return false; } - } else if (HasAttribute(line, kAttributeIceUfrag)) { - if (!GetValue(line, kAttributeIceUfrag, &(session_td->ice_ufrag), + } else if (HasAttribute(*aline, kAttributeIceUfrag)) { + if (!GetValue(*aline, kAttributeIceUfrag, &(session_td->ice_ufrag), error)) { return false; } - } else if (HasAttribute(line, kAttributeIcePwd)) { - if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) { + } else if (HasAttribute(*aline, kAttributeIcePwd)) { + if (!GetValue(*aline, kAttributeIcePwd, &(session_td->ice_pwd), error)) { return false; } - } else if (HasAttribute(line, kAttributeIceLite)) { + } else if (HasAttribute(*aline, kAttributeIceLite)) { session_td->ice_mode = cricket::ICEMODE_LITE; - } else if (HasAttribute(line, kAttributeIceOption)) { - if (!ParseIceOptions(line, &(session_td->transport_options), error)) { + } else if (HasAttribute(*aline, kAttributeIceOption)) { + if (!ParseIceOptions(*aline, &(session_td->transport_options), error)) { return false; } - } else if (HasAttribute(line, kAttributeFingerprint)) { + } else if (HasAttribute(*aline, kAttributeFingerprint)) { if (session_td->identity_fingerprint.get()) { return ParseFailed( - line, + *aline, "Can't have multiple fingerprint attributes at the same level.", error); } std::unique_ptr fingerprint; - if (!ParseFingerprintAttribute(line, &fingerprint, error)) { + if (!ParseFingerprintAttribute(*aline, &fingerprint, error)) { return false; } session_td->identity_fingerprint = std::move(fingerprint); - } else if (HasAttribute(line, kAttributeSetup)) { - if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) { + } else if (HasAttribute(*aline, kAttributeSetup)) { + if (!ParseDtlsSetup(*aline, &(session_td->connection_role), error)) { return false; } - } else if (HasAttribute(line, kAttributeMsidSemantics)) { + } else if (HasAttribute(*aline, kAttributeMsidSemantics)) { std::string semantics; - if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) { + if (!GetValue(*aline, kAttributeMsidSemantics, &semantics, error)) { return false; } desc->set_msid_supported( CaseInsensitiveFind(semantics, kMediaStreamSemantic)); - } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) { + } else if (HasAttribute(*aline, kAttributeExtmapAllowMixed)) { desc->set_extmap_allow_mixed(true); - } else if (HasAttribute(line, kAttributeExtmap)) { + } else if (HasAttribute(*aline, kAttributeExtmap)) { RtpExtension extmap; - if (!ParseExtmap(line, &extmap, error)) { + if (!ParseExtmap(*aline, &extmap, error)) { return false; } session_extmaps->push_back(extmap); @@ -2273,7 +2255,7 @@ bool ParseSessionDescription(const std::string& message, return true; } -bool ParseGroupAttribute(const std::string& line, +bool ParseGroupAttribute(absl::string_view line, cricket::SessionDescription* desc, SdpParseError* error) { RTC_DCHECK(desc != NULL); @@ -2295,7 +2277,7 @@ bool ParseGroupAttribute(const std::string& line, } static bool ParseFingerprintAttribute( - const std::string& line, + absl::string_view line, std::unique_ptr* fingerprint, SdpParseError* error) { std::vector fields; @@ -2326,7 +2308,7 @@ static bool ParseFingerprintAttribute( return true; } -static bool ParseDtlsSetup(const std::string& line, +static bool ParseDtlsSetup(absl::string_view line, cricket::ConnectionRole* role, SdpParseError* error) { // setup-attr = "a=setup:" role @@ -2344,7 +2326,7 @@ static bool ParseDtlsSetup(const std::string& line, return true; } -static bool ParseMsidAttribute(const std::string& line, +static bool ParseMsidAttribute(absl::string_view line, std::vector* stream_ids, std::string* track_id, SdpParseError* error) { @@ -2582,10 +2564,10 @@ void MaybeCreateStaticPayloadAudioCodecs(const std::vector& fmts, template static std::unique_ptr ParseContentDescription( - const std::string& message, + absl::string_view message, const cricket::MediaType media_type, int mline_index, - const std::string& protocol, + absl::string_view protocol, const std::vector& payload_types, size_t* pos, std::string* content_name, @@ -2621,7 +2603,7 @@ static std::unique_ptr ParseContentDescription( } bool ParseMediaDescription( - const std::string& message, + absl::string_view message, const TransportDescription& session_td, const RtpHeaderExtensions& session_extmaps, size_t* pos, @@ -2630,22 +2612,23 @@ bool ParseMediaDescription( std::vector>* candidates, SdpParseError* error) { RTC_DCHECK(desc != NULL); - std::string line; int mline_index = -1; int msid_signaling = 0; // Zero or more media descriptions // RFC 4566 // m= - while (GetLineWithType(message, pos, &line, kLineTypeMedia)) { + while (absl::optional mline = + GetLineWithType(message, pos, kLineTypeMedia)) { ++mline_index; std::vector fields; - rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields); + rtc::split(mline->substr(kLinePrefixLength), kSdpDelimiterSpaceChar, + &fields); const size_t expected_min_fields = 4; if (fields.size() < expected_min_fields) { - return ParseFailedExpectMinFieldNum(line, expected_min_fields, error); + return ParseFailedExpectMinFieldNum(*mline, expected_min_fields, error); } bool port_rejected = false; // RFC 3264 @@ -2657,7 +2640,7 @@ bool ParseMediaDescription( int port = 0; if (!rtc::FromString(fields[1], &port) || !IsValidPort(port)) { - return ParseFailed(line, "The port number is invalid", error); + return ParseFailed(*mline, "The port number is invalid", error); } const std::string& protocol = fields[2]; @@ -2666,7 +2649,7 @@ bool ParseMediaDescription( if (cricket::IsRtpProtocol(protocol)) { for (size_t j = 3; j < fields.size(); ++j) { int pl = 0; - if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) { + if (!GetPayloadTypeFromString(*mline, fields[j], &pl, error)) { return false; } payload_types.push_back(pl); @@ -2687,7 +2670,7 @@ bool ParseMediaDescription( const std::string& media_type = fields[0]; if ((media_type == kMediaTypeVideo || media_type == kMediaTypeAudio) && !cricket::IsRtpProtocol(protocol)) { - return ParseFailed(line, "Unsupported protocol for media type", error); + return ParseFailed(*mline, "Unsupported protocol for media type", error); } if (media_type == kMediaTypeVideo) { content = ParseContentDescription( @@ -2726,10 +2709,11 @@ bool ParseMediaDescription( data_desc->set_protocol(protocol); content = std::move(data_desc); } else { - return ParseFailed(line, "Unsupported protocol for media type", error); + return ParseFailed(*mline, "Unsupported protocol for media type", + error); } } else { - RTC_LOG(LS_WARNING) << "Unsupported media type: " << line; + RTC_LOG(LS_WARNING) << "Unsupported media type: " << *mline; auto unsupported_desc = std::make_unique(media_type); if (!ParseContent(message, cricket::MEDIA_TYPE_UNSUPPORTED, mline_index, @@ -2918,7 +2902,7 @@ void UpdateCodec(MediaContentDescription* content_desc, // according to `packetization`. void UpdateVideoCodecPacketization(VideoContentDescription* video_desc, int payload_type, - const std::string& packetization) { + absl::string_view packetization) { if (packetization != cricket::kPacketizationParamRaw) { // Ignore unsupported packetization attribute. return; @@ -2927,7 +2911,7 @@ void UpdateVideoCodecPacketization(VideoContentDescription* video_desc, // Codec might already have been populated (from rtpmap). cricket::VideoCodec codec = GetCodecWithPayloadType(video_desc->codecs(), payload_type); - codec.packetization = packetization; + codec.packetization = std::string(packetization); AddOrReplaceCodec(video_desc, codec); } @@ -2958,22 +2942,22 @@ void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl* desc) { } void AddAudioAttribute(const std::string& name, - const std::string& value, + absl::string_view value, AudioContentDescription* audio_desc) { if (value.empty()) { return; } std::vector codecs = audio_desc->codecs(); for (cricket::AudioCodec& codec : codecs) { - codec.params[name] = value; + codec.params[name] = std::string(value); } audio_desc->set_codecs(codecs); } -bool ParseContent(const std::string& message, +bool ParseContent(absl::string_view message, const cricket::MediaType media_type, int mline_index, - const std::string& protocol, + absl::string_view protocol, const std::vector& payload_types, size_t* pos, std::string* content_name, @@ -2994,7 +2978,6 @@ bool ParseContent(const std::string& message, // The media level "ice-ufrag" and "ice-pwd". // The candidates before update the media level "ice-pwd" and "ice-ufrag". Candidates candidates_orig; - std::string line; std::string mline_id; // Tracks created out of the ssrc attributes. StreamParamsVec tracks; @@ -3010,7 +2993,8 @@ bool ParseContent(const std::string& message, // Loop until the next m line while (!IsLineType(message, kLineTypeMedia, *pos)) { - if (!GetLine(message, pos, &line)) { + absl::optional line = GetLine(message, pos); + if (!line.has_value()) { if (*pos >= message.size()) { break; // Done parsing } else { @@ -3020,14 +3004,14 @@ bool ParseContent(const std::string& message, // RFC 4566 // b=* (zero or more bandwidth information lines) - if (IsLineType(line, kLineTypeSessionBandwidth)) { + if (IsLineType(*line, kLineTypeSessionBandwidth)) { std::string bandwidth; std::string bandwidth_type; - if (!rtc::tokenize_first(line.substr(kLinePrefixLength), + if (!rtc::tokenize_first(line->substr(kLinePrefixLength), kSdpDelimiterColonChar, &bandwidth_type, &bandwidth)) { return ParseFailed( - line, + *line, "b= syntax error, does not match b=:.", error); } @@ -3037,7 +3021,7 @@ bool ParseContent(const std::string& message, continue; } int b = 0; - if (!GetValueFromString(line, bandwidth, &b, error)) { + if (!GetValueFromString(*line, bandwidth, &b, error)) { return false; } // TODO(deadbeef): Historically, applications may be setting a value @@ -3052,7 +3036,7 @@ bool ParseContent(const std::string& message, } if (b < 0) { return ParseFailed( - line, "b=" + bandwidth_type + " value can't be negative.", error); + *line, "b=" + bandwidth_type + " value can't be negative.", error); } // Convert values. Prevent integer overflow. if (bandwidth_type == kApplicationSpecificBandwidth) { @@ -3066,36 +3050,36 @@ bool ParseContent(const std::string& message, } // Parse the media level connection data. - if (IsLineType(line, kLineTypeConnection)) { + if (IsLineType(*line, kLineTypeConnection)) { rtc::SocketAddress addr; - if (!ParseConnectionData(line, &addr, error)) { + if (!ParseConnectionData(*line, &addr, error)) { return false; } media_desc->set_connection_address(addr); continue; } - if (!IsLineType(line, kLineTypeAttributes)) { + if (!IsLineType(*line, kLineTypeAttributes)) { // TODO(deadbeef): Handle other lines if needed. - RTC_LOG(LS_VERBOSE) << "Ignored line: " << line; + RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line; continue; } // Handle attributes common to SCTP and RTP. - if (HasAttribute(line, kAttributeMid)) { + if (HasAttribute(*line, kAttributeMid)) { // RFC 3388 // mid-attribute = "a=mid:" identification-tag // identification-tag = token // Use the mid identification-tag as the content name. - if (!GetSingleTokenValue(line, kAttributeMid, &mline_id, error)) { + if (!GetSingleTokenValue(*line, kAttributeMid, &mline_id, error)) { return false; } *content_name = mline_id; - } else if (HasAttribute(line, kAttributeBundleOnly)) { + } else if (HasAttribute(*line, kAttributeBundleOnly)) { *bundle_only = true; - } else if (HasAttribute(line, kAttributeCandidate)) { + } else if (HasAttribute(*line, kAttributeCandidate)) { Candidate candidate; - if (!ParseCandidate(line, &candidate, error, false)) { + if (!ParseCandidate(*line, &candidate, error, false)) { return false; } // ParseCandidate will parse non-standard ufrag and password attributes, @@ -3105,30 +3089,30 @@ bool ParseContent(const std::string& message, candidate.set_username(std::string()); candidate.set_password(std::string()); candidates_orig.push_back(candidate); - } else if (HasAttribute(line, kAttributeIceUfrag)) { - if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) { + } else if (HasAttribute(*line, kAttributeIceUfrag)) { + if (!GetValue(*line, kAttributeIceUfrag, &transport->ice_ufrag, error)) { return false; } - } else if (HasAttribute(line, kAttributeIcePwd)) { - if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) { + } else if (HasAttribute(*line, kAttributeIcePwd)) { + if (!GetValue(*line, kAttributeIcePwd, &transport->ice_pwd, error)) { return false; } - } else if (HasAttribute(line, kAttributeIceOption)) { - if (!ParseIceOptions(line, &transport->transport_options, error)) { + } else if (HasAttribute(*line, kAttributeIceOption)) { + if (!ParseIceOptions(*line, &transport->transport_options, error)) { return false; } - } else if (HasAttribute(line, kAttributeFmtp)) { - if (!ParseFmtpAttributes(line, media_type, media_desc, error)) { + } else if (HasAttribute(*line, kAttributeFmtp)) { + if (!ParseFmtpAttributes(*line, media_type, media_desc, error)) { return false; } - } else if (HasAttribute(line, kAttributeFingerprint)) { + } else if (HasAttribute(*line, kAttributeFingerprint)) { std::unique_ptr fingerprint; - if (!ParseFingerprintAttribute(line, &fingerprint, error)) { + if (!ParseFingerprintAttribute(*line, &fingerprint, error)) { return false; } transport->identity_fingerprint = std::move(fingerprint); - } else if (HasAttribute(line, kAttributeSetup)) { - if (!ParseDtlsSetup(line, &(transport->connection_role), error)) { + } else if (HasAttribute(*line, kAttributeSetup)) { + if (!ParseDtlsSetup(*line, &(transport->connection_role), error)) { return false; } } else if (cricket::IsDtlsSctp(protocol) && @@ -3136,23 +3120,23 @@ bool ParseContent(const std::string& message, // // SCTP specific attributes // - if (HasAttribute(line, kAttributeSctpPort)) { + if (HasAttribute(*line, kAttributeSctpPort)) { if (media_desc->as_sctp()->use_sctpmap()) { return ParseFailed( - line, "sctp-port attribute can't be used with sctpmap.", error); + *line, "sctp-port attribute can't be used with sctpmap.", error); } int sctp_port; - if (!ParseSctpPort(line, &sctp_port, error)) { + if (!ParseSctpPort(*line, &sctp_port, error)) { return false; } media_desc->as_sctp()->set_port(sctp_port); - } else if (HasAttribute(line, kAttributeMaxMessageSize)) { + } else if (HasAttribute(*line, kAttributeMaxMessageSize)) { int max_message_size; - if (!ParseSctpMaxMessageSize(line, &max_message_size, error)) { + if (!ParseSctpMaxMessageSize(*line, &max_message_size, error)) { return false; } media_desc->as_sctp()->set_max_message_size(max_message_size); - } else if (HasAttribute(line, kAttributeSctpmap)) { + } else if (HasAttribute(*line, kAttributeSctpmap)) { // Ignore a=sctpmap: from early versions of draft-ietf-mmusic-sctp-sdp continue; } @@ -3160,132 +3144,133 @@ bool ParseContent(const std::string& message, // // RTP specific attributes // - if (HasAttribute(line, kAttributeRtcpMux)) { + if (HasAttribute(*line, kAttributeRtcpMux)) { media_desc->set_rtcp_mux(true); - } else if (HasAttribute(line, kAttributeRtcpReducedSize)) { + } else if (HasAttribute(*line, kAttributeRtcpReducedSize)) { media_desc->set_rtcp_reduced_size(true); - } else if (HasAttribute(line, kAttributeRtcpRemoteEstimate)) { + } else if (HasAttribute(*line, kAttributeRtcpRemoteEstimate)) { media_desc->set_remote_estimate(true); - } else if (HasAttribute(line, kAttributeSsrcGroup)) { - if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) { + } else if (HasAttribute(*line, kAttributeSsrcGroup)) { + if (!ParseSsrcGroupAttribute(*line, &ssrc_groups, error)) { return false; } - } else if (HasAttribute(line, kAttributeSsrc)) { - if (!ParseSsrcAttribute(line, &ssrc_infos, msid_signaling, error)) { + } else if (HasAttribute(*line, kAttributeSsrc)) { + if (!ParseSsrcAttribute(*line, &ssrc_infos, msid_signaling, error)) { return false; } - } else if (HasAttribute(line, kAttributeCrypto)) { - if (!ParseCryptoAttribute(line, media_desc, error)) { + } else if (HasAttribute(*line, kAttributeCrypto)) { + if (!ParseCryptoAttribute(*line, media_desc, error)) { return false; } - } else if (HasAttribute(line, kAttributeRtpmap)) { - if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc, + } else if (HasAttribute(*line, kAttributeRtpmap)) { + if (!ParseRtpmapAttribute(*line, media_type, payload_types, media_desc, error)) { return false; } - } else if (HasAttribute(line, kCodecParamMaxPTime)) { - if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) { + } else if (HasAttribute(*line, kCodecParamMaxPTime)) { + if (!GetValue(*line, kCodecParamMaxPTime, &maxptime_as_string, error)) { return false; } - } else if (HasAttribute(line, kAttributePacketization)) { - if (!ParsePacketizationAttribute(line, media_type, media_desc, error)) { + } else if (HasAttribute(*line, kAttributePacketization)) { + if (!ParsePacketizationAttribute(*line, media_type, media_desc, + error)) { return false; } - } else if (HasAttribute(line, kAttributeRtcpFb)) { - if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) { + } else if (HasAttribute(*line, kAttributeRtcpFb)) { + if (!ParseRtcpFbAttribute(*line, media_type, media_desc, error)) { return false; } - } else if (HasAttribute(line, kCodecParamPTime)) { - if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) { + } else if (HasAttribute(*line, kCodecParamPTime)) { + if (!GetValue(*line, kCodecParamPTime, &ptime_as_string, error)) { return false; } - } else if (HasAttribute(line, kAttributeSendOnly)) { + } else if (HasAttribute(*line, kAttributeSendOnly)) { media_desc->set_direction(RtpTransceiverDirection::kSendOnly); - } else if (HasAttribute(line, kAttributeRecvOnly)) { + } else if (HasAttribute(*line, kAttributeRecvOnly)) { media_desc->set_direction(RtpTransceiverDirection::kRecvOnly); - } else if (HasAttribute(line, kAttributeInactive)) { + } else if (HasAttribute(*line, kAttributeInactive)) { media_desc->set_direction(RtpTransceiverDirection::kInactive); - } else if (HasAttribute(line, kAttributeSendRecv)) { + } else if (HasAttribute(*line, kAttributeSendRecv)) { media_desc->set_direction(RtpTransceiverDirection::kSendRecv); - } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) { + } else if (HasAttribute(*line, kAttributeExtmapAllowMixed)) { media_desc->set_extmap_allow_mixed_enum( MediaContentDescription::kMedia); - } else if (HasAttribute(line, kAttributeExtmap)) { + } else if (HasAttribute(*line, kAttributeExtmap)) { RtpExtension extmap; - if (!ParseExtmap(line, &extmap, error)) { + if (!ParseExtmap(*line, &extmap, error)) { return false; } media_desc->AddRtpHeaderExtension(extmap); - } else if (HasAttribute(line, kAttributeXGoogleFlag)) { + } else if (HasAttribute(*line, kAttributeXGoogleFlag)) { // Experimental attribute. Conference mode activates more aggressive // AEC and NS settings. // TODO(deadbeef): expose API to set these directly. std::string flag_value; - if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) { + if (!GetValue(*line, kAttributeXGoogleFlag, &flag_value, error)) { return false; } if (flag_value.compare(kValueConference) == 0) media_desc->set_conference_mode(true); - } else if (HasAttribute(line, kAttributeMsid)) { - if (!ParseMsidAttribute(line, &stream_ids, &track_id, error)) { + } else if (HasAttribute(*line, kAttributeMsid)) { + if (!ParseMsidAttribute(*line, &stream_ids, &track_id, error)) { return false; } *msid_signaling |= cricket::kMsidSignalingMediaSection; - } else if (HasAttribute(line, kAttributeRid)) { + } else if (HasAttribute(*line, kAttributeRid)) { const size_t kRidPrefixLength = kLinePrefixLength + arraysize(kAttributeRid); - if (line.size() <= kRidPrefixLength) { - RTC_LOG(LS_INFO) << "Ignoring empty RID attribute: " << line; + if (line->size() <= kRidPrefixLength) { + RTC_LOG(LS_INFO) << "Ignoring empty RID attribute: " << *line; continue; } RTCErrorOr error_or_rid_description = deserializer.DeserializeRidDescription( - line.substr(kRidPrefixLength)); + line->substr(kRidPrefixLength)); // Malformed a=rid lines are discarded. if (!error_or_rid_description.ok()) { - RTC_LOG(LS_INFO) << "Ignoring malformed RID line: '" << line + RTC_LOG(LS_INFO) << "Ignoring malformed RID line: '" << *line << "'. Error: " << error_or_rid_description.error().message(); continue; } rids.push_back(error_or_rid_description.MoveValue()); - } else if (HasAttribute(line, kAttributeSimulcast)) { + } else if (HasAttribute(*line, kAttributeSimulcast)) { const size_t kSimulcastPrefixLength = kLinePrefixLength + arraysize(kAttributeSimulcast); - if (line.size() <= kSimulcastPrefixLength) { - return ParseFailed(line, "Simulcast attribute is empty.", error); + if (line->size() <= kSimulcastPrefixLength) { + return ParseFailed(*line, "Simulcast attribute is empty.", error); } if (!simulcast.empty()) { - return ParseFailed(line, "Multiple Simulcast attributes specified.", + return ParseFailed(*line, "Multiple Simulcast attributes specified.", error); } RTCErrorOr error_or_simulcast = deserializer.DeserializeSimulcastDescription( - line.substr(kSimulcastPrefixLength)); + line->substr(kSimulcastPrefixLength)); if (!error_or_simulcast.ok()) { - return ParseFailed(line, + return ParseFailed(*line, std::string("Malformed simulcast line: ") + error_or_simulcast.error().message(), error); } simulcast = error_or_simulcast.value(); - } else if (HasAttribute(line, kAttributeRtcp)) { + } else if (HasAttribute(*line, kAttributeRtcp)) { // Ignore and do not log a=rtcp line. // JSEP section 5.8.2 (media section parsing) says to ignore it. continue; } else { // Unrecognized attribute in RTP protocol. - RTC_LOG(LS_VERBOSE) << "Ignored line: " << line; + RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line; continue; } } else { // Only parse lines that we are interested of. - RTC_LOG(LS_VERBOSE) << "Ignored line: " << line; + RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line; continue; } } @@ -3399,7 +3384,7 @@ bool ParseContent(const std::string& message, return true; } -bool ParseSsrcAttribute(const std::string& line, +bool ParseSsrcAttribute(absl::string_view line, SsrcInfoVec* ssrc_infos, int* msid_signaling, SdpParseError* error) { @@ -3467,19 +3452,13 @@ bool ParseSsrcAttribute(const std::string& line, ssrc_info.track_id = fields[1]; } *msid_signaling |= cricket::kMsidSignalingSsrcAttribute; - } else if (attribute == kSsrcAttributeMslabel) { - // draft-alvestrand-rtcweb-mid-01 - // mslabel: - ssrc_info.mslabel = value; - } else if (attribute == kSSrcAttributeLabel) { - // The label isn't defined. - // label: - ssrc_info.label = value; + } else { + RTC_LOG(LS_INFO) << "Ignored unknown ssrc-specific attribute: " << line; } return true; } -bool ParseSsrcGroupAttribute(const std::string& line, +bool ParseSsrcGroupAttribute(absl::string_view line, SsrcGroupVec* ssrc_groups, SdpParseError* error) { RTC_DCHECK(ssrc_groups != NULL); @@ -3507,7 +3486,7 @@ bool ParseSsrcGroupAttribute(const std::string& line, return true; } -bool ParseCryptoAttribute(const std::string& line, +bool ParseCryptoAttribute(absl::string_view line, MediaContentDescription* media_desc, SdpParseError* error) { std::vector fields; @@ -3540,7 +3519,7 @@ bool ParseCryptoAttribute(const std::string& line, // Updates or creates a new codec entry in the audio description with according // to `name`, `clockrate`, `bitrate`, and `channels`. void UpdateCodec(int payload_type, - const std::string& name, + absl::string_view name, int clockrate, int bitrate, size_t channels, @@ -3549,7 +3528,7 @@ void UpdateCodec(int payload_type, // (from an fmtp). cricket::AudioCodec codec = GetCodecWithPayloadType(audio_desc->codecs(), payload_type); - codec.name = name; + codec.name = std::string(name); codec.clockrate = clockrate; codec.bitrate = bitrate; codec.channels = channels; @@ -3560,18 +3539,18 @@ void UpdateCodec(int payload_type, // Updates or creates a new codec entry in the video description according to // `name`, `width`, `height`, and `framerate`. void UpdateCodec(int payload_type, - const std::string& name, + absl::string_view name, VideoContentDescription* video_desc) { // Codec may already be populated with (only) optional parameters // (from an fmtp). cricket::VideoCodec codec = GetCodecWithPayloadType(video_desc->codecs(), payload_type); - codec.name = name; + codec.name = std::string(name); AddOrReplaceCodec(video_desc, codec); } -bool ParseRtpmapAttribute(const std::string& line, +bool ParseRtpmapAttribute(absl::string_view line, const cricket::MediaType media_type, const std::vector& payload_types, MediaContentDescription* media_desc, @@ -3642,21 +3621,21 @@ bool ParseRtpmapAttribute(const std::string& line, return true; } -bool ParseFmtpParam(const std::string& line, +bool ParseFmtpParam(absl::string_view line, std::string* parameter, std::string* value, SdpParseError* error) { if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) { // Support for non-key-value lines like RFC 2198 or RFC 4733. *parameter = ""; - *value = line; + *value = std::string(line); return true; } // a=fmtp: =; =; ... return true; } -bool ParseFmtpAttributes(const std::string& line, +bool ParseFmtpAttributes(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error) { @@ -3692,14 +3671,13 @@ bool ParseFmtpAttributes(const std::string& line, } // Parse out format specific parameters. - std::vector fields; - rtc::split(line_params, kSdpDelimiterSemicolonChar, &fields); - cricket::CodecParameterMap codec_params; - for (auto& iter : fields) { + for (absl::string_view param : + rtc::split(line_params, kSdpDelimiterSemicolonChar)) { std::string name; std::string value; - if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) { + if (!ParseFmtpParam(absl::StripAsciiWhitespace(param), &name, &value, + error)) { return false; } if (codec_params.find(name) != codec_params.end()) { @@ -3719,7 +3697,7 @@ bool ParseFmtpAttributes(const std::string& line, return true; } -bool ParsePacketizationAttribute(const std::string& line, +bool ParsePacketizationAttribute(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error) { @@ -3727,7 +3705,7 @@ bool ParsePacketizationAttribute(const std::string& line, return true; } std::vector packetization_fields; - rtc::split(line.c_str(), kSdpDelimiterSpaceChar, &packetization_fields); + rtc::split(line, kSdpDelimiterSpaceChar, &packetization_fields); if (packetization_fields.size() < 2) { return ParseFailedGetValue(line, kAttributePacketization, error); } @@ -3747,7 +3725,7 @@ bool ParsePacketizationAttribute(const std::string& line, return true; } -bool ParseRtcpFbAttribute(const std::string& line, +bool ParseRtcpFbAttribute(absl::string_view line, const cricket::MediaType media_type, MediaContentDescription* media_desc, SdpParseError* error) { @@ -3756,7 +3734,7 @@ bool ParseRtcpFbAttribute(const std::string& line, return true; } std::vector rtcp_fb_fields; - rtc::split(line.c_str(), kSdpDelimiterSpaceChar, &rtcp_fb_fields); + rtc::split(line, kSdpDelimiterSpaceChar, &rtcp_fb_fields); if (rtcp_fb_fields.size() < 2) { return ParseFailedGetValue(line, kAttributeRtcpFb, error); } diff --git a/pc/webrtc_sdp.h b/pc/webrtc_sdp.h index 6d6980a989..cc6813caec 100644 --- a/pc/webrtc_sdp.h +++ b/pc/webrtc_sdp.h @@ -22,6 +22,7 @@ #include +#include "absl/strings/string_view.h" #include "api/candidate.h" #include "api/jsep.h" #include "api/jsep_ice_candidate.h" @@ -66,7 +67,7 @@ RTC_EXPORT std::string SdpSerializeCandidate( // jdesc - The JsepSessionDescription deserialized from the SDP string. // error - The detail error information when parsing fails. // return - true on success, false on failure. -bool SdpDeserialize(const std::string& message, +bool SdpDeserialize(absl::string_view message, JsepSessionDescription* jdesc, SdpParseError* error); @@ -77,7 +78,7 @@ bool SdpDeserialize(const std::string& message, // candidates - The JsepIceCandidate from the SDP string. // error - The detail error information when parsing fails. // return - true on success, false on failure. -RTC_EXPORT bool SdpDeserializeCandidate(const std::string& message, +RTC_EXPORT bool SdpDeserializeCandidate(absl::string_view message, JsepIceCandidate* candidate, SdpParseError* error); @@ -89,8 +90,8 @@ RTC_EXPORT bool SdpDeserializeCandidate(const std::string& message, // candidate - The cricket Candidate from the SDP string. // error - The detail error information when parsing fails. // return - true on success, false on failure. -RTC_EXPORT bool SdpDeserializeCandidate(const std::string& transport_name, - const std::string& message, +RTC_EXPORT bool SdpDeserializeCandidate(absl::string_view transport_name, + absl::string_view message, cricket::Candidate* candidate, SdpParseError* error); @@ -100,7 +101,7 @@ RTC_EXPORT bool SdpDeserializeCandidate(const std::string& transport_name, // `error` is not null. // If `is_raw` is false, `message` is expected to be prefixed with "a=". // If `is_raw` is true, no prefix is expected in `messaage`. -RTC_EXPORT bool ParseCandidate(const std::string& message, +RTC_EXPORT bool ParseCandidate(absl::string_view message, cricket::Candidate* candidate, SdpParseError* error, bool is_raw); diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc index 21d682315e..86e137ee1a 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -11,10 +11,10 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -22,21 +22,26 @@ #include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "api/array_view.h" #include "api/crypto_params.h" #include "api/jsep_session_description.h" #include "api/media_types.h" #include "api/rtp_parameters.h" -#include "api/rtp_transceiver_interface.h" +#include "api/rtp_transceiver_direction.h" #include "media/base/codec.h" #include "media/base/media_constants.h" +#include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/port.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" +#include "pc/media_protocol_names.h" #include "pc/media_session.h" #include "pc/session_description.h" +#include "pc/simulcast_description.h" #include "rtc_base/checks.h" #include "rtc_base/message_digest.h" #include "rtc_base/socket_address.h" @@ -183,8 +188,6 @@ static const char kSdpFullString[] = "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream_1\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n" "m=video 3457 RTP/SAVPF 120\r\n" "c=IN IP4 74.125.224.39\r\n" "a=rtcp:3456 IN IP4 74.125.224.39\r\n" @@ -209,12 +212,8 @@ static const char kSdpFullString[] = "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:2 mslabel:local_stream_1\r\n" - "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" - "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_1\r\n"; + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"; // SDP reference string without the candidates. static const char kSdpString[] = @@ -240,8 +239,6 @@ static const char kSdpString[] = "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream_1\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n" "m=video 9 RTP/SAVPF 120\r\n" "c=IN IP4 0.0.0.0\r\n" "a=rtcp:9 IN IP4 0.0.0.0\r\n" @@ -254,12 +251,8 @@ static const char kSdpString[] = "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:2 mslabel:local_stream_1\r\n" - "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" - "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_1\r\n"; + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"; // draft-ietf-mmusic-sctp-sdp-03 static const char kSdpSctpDataChannelString[] = @@ -334,9 +327,7 @@ static const char kSdpAudioString[] = "a=sendrecv\r\n" "a=rtpmap:111 opus/48000/2\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" - "a=ssrc:1 msid:local_stream audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n"; + "a=ssrc:1 msid:local_stream audio_track_id_1\r\n"; static const char kSdpVideoString[] = "m=video 9 RTP/SAVPF 120\r\n" @@ -347,9 +338,7 @@ static const char kSdpVideoString[] = "a=sendrecv\r\n" "a=rtpmap:120 VP8/90000\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" - "a=ssrc:2 msid:local_stream video_track_id_1\r\n" - "a=ssrc:2 mslabel:local_stream\r\n" - "a=ssrc:2 label:video_track_id_1\r\n"; + "a=ssrc:2 msid:local_stream video_track_id_1\r\n"; // Reference sdp string using bundle-only. static const char kBundleOnlySdpFullString[] = @@ -390,8 +379,6 @@ static const char kBundleOnlySdpFullString[] = "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream_1\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n" "m=video 0 RTP/SAVPF 120\r\n" "c=IN IP4 0.0.0.0\r\n" "a=rtcp:9 IN IP4 0.0.0.0\r\n" @@ -404,12 +391,8 @@ static const char kBundleOnlySdpFullString[] = "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:2 mslabel:local_stream_1\r\n" - "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" - "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_1\r\n"; + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n"; // Plan B SDP reference string, with 2 streams, 2 audio tracks and 3 video // tracks. @@ -450,12 +433,8 @@ static const char kPlanBSdpFullString[] = "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream_1\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n" "a=ssrc:4 cname:stream_2_cname\r\n" "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n" - "a=ssrc:4 mslabel:local_stream_2\r\n" - "a=ssrc:4 label:audio_track_id_2\r\n" "m=video 3457 RTP/SAVPF 120\r\n" "c=IN IP4 74.125.224.39\r\n" "a=rtcp:3456 IN IP4 74.125.224.39\r\n" @@ -480,20 +459,12 @@ static const char kPlanBSdpFullString[] = "a=ssrc-group:FEC 2 3\r\n" "a=ssrc:2 cname:stream_1_cname\r\n" "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:2 mslabel:local_stream_1\r\n" - "a=ssrc:2 label:video_track_id_1\r\n" "a=ssrc:3 cname:stream_1_cname\r\n" "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n" - "a=ssrc:3 mslabel:local_stream_1\r\n" - "a=ssrc:3 label:video_track_id_1\r\n" "a=ssrc:5 cname:stream_2_cname\r\n" "a=ssrc:5 msid:local_stream_2 video_track_id_2\r\n" - "a=ssrc:5 mslabel:local_stream_2\r\n" - "a=ssrc:5 label:video_track_id_2\r\n" "a=ssrc:6 cname:stream_2_cname\r\n" - "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n" - "a=ssrc:6 mslabel:local_stream_2\r\n" - "a=ssrc:6 label:video_track_id_3\r\n"; + "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"; // Unified Plan SDP reference string, with 2 streams, 2 audio tracks and 3 video // tracks. @@ -649,8 +620,6 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] = "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:1 cname:stream_1_cname\r\n" "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n" - "a=ssrc:1 mslabel:local_stream_1\r\n" - "a=ssrc:1 label:audio_track_id_1\r\n" // Audio track 2, with two stream ids. "m=audio 9 RTP/SAVPF 111 103 104\r\n" "c=IN IP4 0.0.0.0\r\n" @@ -672,8 +641,6 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] = // The support for Plan B msid signaling only includes the // first media stream id "local_stream_1." "a=ssrc:4 msid:local_stream_1 audio_track_id_2\r\n" - "a=ssrc:4 mslabel:local_stream_1\r\n" - "a=ssrc:4 label:audio_track_id_2\r\n" // Audio track 3, with no stream ids. "m=audio 9 RTP/SAVPF 111 103 104\r\n" "c=IN IP4 0.0.0.0\r\n" @@ -691,9 +658,7 @@ static const char kUnifiedPlanSdpFullStringWithSpecialMsid[] = "a=rtpmap:103 ISAC/16000\r\n" "a=rtpmap:104 ISAC/32000\r\n" "a=ssrc:7 cname:stream_2_cname\r\n" - "a=ssrc:7 msid:- audio_track_id_3\r\n" - "a=ssrc:7 mslabel:-\r\n" - "a=ssrc:7 label:audio_track_id_3\r\n"; + "a=ssrc:7 msid:- audio_track_id_3\r\n"; // SDP string for unified plan without SSRCs static const char kUnifiedPlanSdpFullStringNoSsrc[] = @@ -2662,21 +2627,6 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) { EXPECT_TRUE(TestDeserializeRejected(true, true)); } -// Tests that we can still handle the sdp uses mslabel and label instead of -// msid for backward compatibility. -TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) { - jdesc_.description()->set_msid_supported(false); - JsepSessionDescription jdesc(kDummyType); - std::string sdp_without_msid = kSdpFullString; - Replace("msid", "xmsid", &sdp_without_msid); - // Deserialize - EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc)); - // Verify - EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc)); - EXPECT_FALSE(jdesc.description()->msid_signaling() & - ~cricket::kMsidSignalingSsrcAttribute); -} - TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithExtmapAllowMixed) { jdesc_.description()->set_extmap_allow_mixed(true); std::string sdp_with_extmap_allow_mixed = kSdpFullString; @@ -2778,7 +2728,7 @@ TEST_F(WebRtcSdpTest, DeserializeCandidate) { } // This test verifies the deserialization of candidate-attribute -// as per RFC 5245. Candiate-attribute will be of the format +// as per RFC 5245. Candidate-attribute will be of the format // candidate:. This format will be used when candidates // are trickled. TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) { @@ -3113,7 +3063,7 @@ TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutEndLineBreak) { // Deserialize SdpParseError error; EXPECT_FALSE(webrtc::SdpDeserialize(sdp, &jdesc, &error)); - const std::string lastline = "a=ssrc:3 label:video_track_id_1"; + const std::string lastline = "a=ssrc:3 msid:local_stream_1 video_track_id_1"; EXPECT_EQ(lastline, error.line); EXPECT_EQ("Invalid SDP line.", error.description); } @@ -4278,15 +4228,16 @@ TEST_F(WebRtcSdpTest, DeserializeEmptySessionName) { // Simulcast malformed input test for invalid format. TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_EmptyAttribute) { - ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n", - "a=simulcast:\r\n", "a=simulcast:"); + ExpectParseFailureWithNewLines( + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n", "a=simulcast:\r\n", + "a=simulcast:"); } // Tests that duplicate simulcast entries in the SDP triggers a parse failure. TEST_F(WebRtcSdpTest, DeserializeSimulcastNegative_DuplicateAttribute) { - ExpectParseFailureWithNewLines("a=ssrc:3 label:video_track_id_1\r\n", - "a=simulcast:send 1\r\na=simulcast:recv 2\r\n", - "a=simulcast:"); + ExpectParseFailureWithNewLines( + "a=ssrc:3 msid:local_stream_1 video_track_id_1\r\n", + "a=simulcast:send 1\r\na=simulcast:recv 2\r\n", "a=simulcast:"); } // Validates that deserialization uses the a=simulcast: attribute @@ -4706,3 +4657,13 @@ TEST_F(WebRtcSdpTest, MaxChannels) { ExpectParseFailure(sdp, "a=rtpmap:108 ISAC/16000/512"); } + +// This tests parsing of SDP with unknown ssrc-specific attributes. +TEST_F(WebRtcSdpTest, ParseIgnoreUnknownSsrcSpecificAttribute) { + std::string sdp = kSdpString; + sdp += "a=ssrc:1 mslabel:something\r\n"; + + JsepSessionDescription output(kDummyType); + SdpParseError error; + ASSERT_TRUE(webrtc::SdpDeserialize(sdp, &output, &error)); +} diff --git a/pc/webrtc_session_description_factory.cc b/pc/webrtc_session_description_factory.cc index 36130758a3..21da3a7bea 100644 --- a/pc/webrtc_session_description_factory.cc +++ b/pc/webrtc_session_description_factory.cc @@ -11,8 +11,8 @@ #include "pc/webrtc_session_description_factory.h" #include -#include -#include + +#include #include #include #include @@ -23,6 +23,8 @@ #include "api/jsep.h" #include "api/jsep_session_description.h" #include "api/rtc_error.h" +#include "api/sequence_checker.h" +#include "pc/connection_context.h" #include "pc/sdp_state_provider.h" #include "pc/session_description.h" #include "rtc_base/checks.h" @@ -32,6 +34,7 @@ #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/string_encode.h" +#include "rtc_base/unique_id_generator.h" using cricket::MediaSessionOptions; using rtc::UniqueRandomIdGenerator; @@ -125,17 +128,19 @@ void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( } WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, + ConnectionContext* context, const SdpStateProvider* sdp_info, const std::string& session_id, bool dtls_enabled, std::unique_ptr cert_generator, const rtc::scoped_refptr& certificate, std::function&)> - on_certificate_ready) - : signaling_thread_(signaling_thread), - session_desc_factory_(channel_manager, &transport_desc_factory_), + on_certificate_ready, + const FieldTrialsView& field_trials) + : signaling_thread_(context->signaling_thread()), + transport_desc_factory_(field_trials), + session_desc_factory_(context->channel_manager(), + &transport_desc_factory_), // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp // as the session id and session version. To simplify, it should be fine // to just use a random number as session id and start version from @@ -166,7 +171,7 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory( // `SignalCertificateReady`. signaling_thread_->Post( RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE, - new rtc::ScopedRefMessageData(certificate)); + new rtc::ScopedRefMessageData(certificate.get())); } else { // Generate certificate. certificate_request_state_ = CERTIFICATE_WAITING; @@ -345,7 +350,7 @@ void WebRtcSessionDescriptionFactory::InternalCreateOffer( ? sdp_info_->local_description()->description() : nullptr); if (!desc) { - PostCreateSessionDescriptionFailed(request.observer, + PostCreateSessionDescriptionFailed(request.observer.get(), "Failed to initialize the offer."); return; } @@ -372,7 +377,8 @@ void WebRtcSessionDescriptionFactory::InternalCreateOffer( } } } - PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer)); + PostCreateSessionDescriptionSucceeded(request.observer.get(), + std::move(offer)); } void WebRtcSessionDescriptionFactory::InternalCreateAnswer( @@ -406,7 +412,7 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer( ? sdp_info_->local_description()->description() : nullptr); if (!desc) { - PostCreateSessionDescriptionFailed(request.observer, + PostCreateSessionDescriptionFailed(request.observer.get(), "Failed to initialize the answer."); return; } @@ -433,7 +439,8 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer( } } } - PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer)); + PostCreateSessionDescriptionSucceeded(request.observer.get(), + std::move(answer)); } void WebRtcSessionDescriptionFactory::FailPendingRequests( @@ -443,7 +450,7 @@ void WebRtcSessionDescriptionFactory::FailPendingRequests( const CreateSessionDescriptionRequest& request = create_session_description_requests_.front(); PostCreateSessionDescriptionFailed( - request.observer, + request.observer.get(), ((request.type == CreateSessionDescriptionRequest::kOffer) ? "CreateOffer" : "CreateAnswer") + diff --git a/pc/webrtc_session_description_factory.h b/pc/webrtc_session_description_factory.h index 6a6e8efa51..20c9100590 100644 --- a/pc/webrtc_session_description_factory.h +++ b/pc/webrtc_session_description_factory.h @@ -23,7 +23,6 @@ #include "api/scoped_refptr.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" -#include "pc/channel_manager.h" #include "pc/media_session.h" #include "pc/sdp_state_provider.h" #include "rtc_base/message_handler.h" @@ -79,15 +78,15 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler, // asynchronously. If a certificate is given, will use that for identifying // over DTLS. If neither is specified, DTLS is disabled. WebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, + ConnectionContext* context, const SdpStateProvider* sdp_info, const std::string& session_id, bool dtls_enabled, std::unique_ptr cert_generator, const rtc::scoped_refptr& certificate, std::function&)> - on_certificate_ready); + on_certificate_ready, + const FieldTrialsView& field_trials); virtual ~WebRtcSessionDescriptionFactory(); WebRtcSessionDescriptionFactory(const WebRtcSessionDescriptionFactory&) = diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 0cac2a4f2b..33cd57f2e2 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -71,127 +71,252 @@ rtc_source_set("callback_list") { "../api:function_view", "system:assume", "system:inline", + "system:rtc_export", ] } -# The subset of rtc_base approved for use outside of libjingle. -# TODO(bugs.webrtc.org/9838): Create small and focused build targets and remove -# the old concept of rtc_base and rtc_base_approved. -rtc_library("rtc_base_approved") { +rtc_source_set("buffer") { visibility = [ "*" ] + sources = [ "buffer.h" ] deps = [ ":checks", - ":rtc_task_queue", - ":safe_compare", ":type_traits", + ":zero_memory", "../api:array_view", - "../api:scoped_refptr", - "../api:sequence_checker", - "synchronization:mutex", - "system:arch", - "system:no_unique_address", - "system:rtc_export", - "third_party/base64", ] - absl_deps = [ - "//third_party/abseil-cpp/absl/base:core_headers", - "//third_party/abseil-cpp/absl/numeric:bits", - "//third_party/abseil-cpp/absl/types:optional", + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_source_set("byte_order") { + visibility = [ "*" ] + sources = [ "byte_order.h" ] + deps = [ "system:arch" ] +} + +rtc_source_set("mod_ops") { + visibility = [ "*" ] + sources = [ "numerics/mod_ops.h" ] + deps = [ ":checks" ] +} + +rtc_source_set("moving_max_counter") { + visibility = [ "*" ] + sources = [ "numerics/moving_max_counter.h" ] + deps = [ ":checks" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_source_set("one_time_event") { + visibility = [ "*" ] + sources = [ "one_time_event.h" ] + deps = [ "synchronization:mutex" ] +} + +rtc_source_set("strong_alias") { + visibility = [ "*" ] + sources = [ "strong_alias.h" ] +} + +rtc_source_set("swap_queue") { + visibility = [ "*" ] + sources = [ "swap_queue.h" ] + deps = [ ":checks" ] + absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] +} + +rtc_source_set("macromagic") { + sources = [ + "arraysize.h", + "thread_annotations.h", ] - public_deps = [] # no-presubmit-check TODO(webrtc:8603) + deps = [ "system:arch" ] +} +rtc_library("bit_buffer") { + visibility = [ "*" ] sources = [ "bit_buffer.cc", "bit_buffer.h", - "buffer.h", - "buffer_queue.cc", - "buffer_queue.h", + ] + deps = [ ":checks" ] + absl_deps = [ "//third_party/abseil-cpp/absl/numeric:bits" ] +} + +rtc_library("byte_buffer") { + visibility = [ "*" ] + sources = [ "byte_buffer.cc", "byte_buffer.h", - "byte_order.h", + ] + deps = [ + ":buffer", + ":byte_order", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("buffer_queue") { + visibility = [ "*" ] + sources = [ + "buffer_queue.cc", + "buffer_queue.h", + ] + deps = [ + ":buffer", + ":macromagic", + "../api:sequence_checker", + "system:no_unique_address", + ] +} + +rtc_library("copy_on_write_buffer") { + visibility = [ "*" ] + sources = [ "copy_on_write_buffer.cc", "copy_on_write_buffer.h", + ] + deps = [ + ":buffer", + ":checks", + ":refcount", + ":type_traits", + "../api:scoped_refptr", + "system:rtc_export", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("event_tracer") { + visibility = [ "*" ] + sources = [ "event_tracer.cc", "event_tracer.h", - "location.cc", - "location.h", + "trace_event.h", + ] + deps = [ + ":atomicops", + ":checks", + ":logging", + ":macromagic", + ":platform_thread", + ":platform_thread_types", + ":rtc_event", + ":timeutils", + "../api:sequence_checker", + "synchronization:mutex", + "system:rtc_export", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("histogram_percentile_counter") { + visibility = [ "*" ] + sources = [ "numerics/histogram_percentile_counter.cc", "numerics/histogram_percentile_counter.h", - "numerics/mod_ops.h", - "numerics/moving_max_counter.h", - "numerics/sample_counter.cc", - "numerics/sample_counter.h", - "one_time_event.h", + ] + deps = [ ":checks" ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("location") { + visibility = [ "*" ] + sources = [ + "location.cc", + "location.h", + ] + deps = [ "system:rtc_export" ] +} + +rtc_library("race_checker") { + visibility = [ "*" ] + sources = [ "race_checker.cc", "race_checker.h", + ] + deps = [ + ":checks", + ":macromagic", + ":platform_thread_types", + ] +} + +rtc_library("random") { + visibility = [ "*" ] + sources = [ "random.cc", "random.h", + ] + deps = [ + ":checks", + ":safe_conversions", + ] +} + +rtc_library("rate_statistics") { + visibility = [ "*" ] + sources = [ "rate_statistics.cc", "rate_statistics.h", + ] + deps = [ + ":checks", + ":logging", + ":safe_conversions", + "system:rtc_export", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("rate_tracker") { + visibility = [ "*" ] + sources = [ "rate_tracker.cc", "rate_tracker.h", - "strong_alias.h", - "swap_queue.h", - "timestamp_aligner.cc", - "timestamp_aligner.h", - "trace_event.h", - "zero_memory.cc", - "zero_memory.h", ] + deps = [ + ":checks", + ":timeutils", + ] +} - if (is_win) { - sources += [ - "win/get_activation_factory.cc", - "win/get_activation_factory.h", - "win/hstring.cc", - "win/hstring.h", - "win/scoped_com_initializer.cc", - "win/scoped_com_initializer.h", - "win/windows_version.cc", - "win/windows_version.h", - ] - 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" ] - } - - if (is_android) { - libs = [ "log" ] - } +rtc_library("sample_counter") { + visibility = [ "*" ] + sources = [ + "numerics/sample_counter.cc", + "numerics/sample_counter.h", + ] + deps = [ + ":checks", + ":safe_conversions", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} - public_deps += [ # no-presubmit-check TODO(webrtc:8603) - ":atomicops", +rtc_library("timestamp_aligner") { + visibility = [ "*" ] + sources = [ + "timestamp_aligner.cc", + "timestamp_aligner.h", + ] + deps = [ + ":checks", ":logging", - ":macromagic", - ":platform_thread", - ":platform_thread_types", - ":refcount", - ":rtc_event", - ":safe_conversions", - ":stringutils", ":timeutils", - "../api:sequence_checker", + "system:rtc_export", ] } -rtc_source_set("macromagic") { +rtc_library("zero_memory") { + visibility = [ "*" ] sources = [ - "arraysize.h", - "format_macros.h", - "thread_annotations.h", + "zero_memory.cc", + "zero_memory.h", + ] + deps = [ + ":checks", + "../api:array_view", ] - deps = [ "system:arch" ] } rtc_library("platform_thread_types") { @@ -231,14 +356,6 @@ rtc_library("criticalsection") { } rtc_library("platform_thread") { - visibility = [ - ":rtc_base_approved", - ":rtc_task_queue_libevent", - ":rtc_task_queue_stdlib", - ":rtc_task_queue_win", - "../api:sequence_checker", - "synchronization:mutex", - ] sources = [ "platform_thread.cc", "platform_thread.h", @@ -350,6 +467,7 @@ rtc_library("checks") { ] deps = [ ":safe_compare", + "../api:scoped_refptr", "system:inline", "system:rtc_export", ] @@ -368,7 +486,8 @@ rtc_library("rate_limiter") { "rate_limiter.h", ] deps = [ - ":rtc_base_approved", + ":macromagic", + ":rate_statistics", "../system_wrappers", "synchronization:mutex", ] @@ -632,7 +751,7 @@ rtc_library("rtc_numerics") { ] deps = [ ":checks", - ":rtc_base_approved", + ":mod_ops", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -674,8 +793,7 @@ rtc_library("rtc_json") { deps = [ ":stringutils" ] all_dependent_configs = [ "//third_party/jsoncpp:jsoncpp_config" ] if (rtc_build_json) { - public_deps = # no-presubmit-check TODO(webrtc:8603) - [ "//third_party/jsoncpp" ] + deps += [ "//third_party/jsoncpp" ] } else { include_dirs = [ "$rtc_jsoncpp_root" ] @@ -683,6 +801,7 @@ rtc_library("rtc_json") { # expected to be when building json outside of the standalone build. defines += [ "WEBRTC_EXTERNAL_JSON" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("net_helpers") { @@ -696,10 +815,11 @@ rtc_library("net_helpers") { } if (is_win) { deps += [ - ":rtc_base_approved", ":win32", + "win:windows_version", ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("async_resolver_interface") { @@ -722,14 +842,15 @@ rtc_library("ip_address") { "ip_address.h", ] deps = [ + ":byte_order", ":net_helpers", - ":rtc_base_approved", ":stringutils", "system:rtc_export", ] if (is_win) { deps += [ ":win32" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("socket_address") { @@ -739,11 +860,11 @@ rtc_library("socket_address") { "socket_address.h", ] deps = [ + ":byte_order", ":checks", ":ip_address", ":logging", ":net_helpers", - ":rtc_base_approved", ":safe_conversions", ":stringutils", "system:rtc_export", @@ -751,6 +872,7 @@ rtc_library("socket_address") { if (is_win) { deps += [ ":win32" ] } + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } rtc_library("null_socket_server") { @@ -795,19 +917,24 @@ rtc_library("threading") { absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/strings", ] deps = [ ":async_resolver_interface", ":atomicops", + ":byte_order", ":checks", ":criticalsection", + ":event_tracer", ":ip_address", + ":location", ":logging", ":macromagic", ":network_constants", ":null_socket_server", + ":platform_thread", ":platform_thread_types", - ":rtc_base_approved", + ":refcount", ":rtc_event", ":rtc_task_queue", ":socket_address", @@ -902,9 +1029,11 @@ if (is_win) { ] deps = [ + ":byte_order", ":checks", + ":logging", ":macromagic", - ":rtc_base_approved", + ":stringutils", ] libs = [ @@ -926,25 +1055,41 @@ rtc_library("rtc_base") { deps = [ ":async_resolver_interface", ":async_socket", + ":buffer", + ":byte_buffer", + ":byte_order", + ":callback_list", ":checks", + ":copy_on_write_buffer", ":ip_address", + ":location", + ":logging", + ":macromagic", ":network_constants", ":null_socket_server", + ":refcount", + ":rtc_event", ":rtc_task_queue", + ":safe_conversions", ":socket", ":socket_address", ":socket_factory", ":socket_server", ":stringutils", ":threading", + ":timeutils", + ":zero_memory", "../api:array_view", + "../api:field_trials_view", "../api:function_view", "../api:refcountedbase", "../api:scoped_refptr", "../api:sequence_checker", "../api/numerics", "../api/task_queue", + "../api/transport:field_trial_based_config", "../system_wrappers:field_trial", + "memory:always_valid_pointer", "network:sent_packet", "synchronization:mutex", "system:file_wrapper", @@ -964,7 +1109,6 @@ rtc_library("rtc_base") { "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] - public_deps = [ ":rtc_base_approved" ] # no-presubmit-check TODO(webrtc:8603) public_configs = [] sources = [ @@ -1215,16 +1359,25 @@ rtc_library("rtc_base_tests_utils") { ] deps = [ ":async_socket", + ":byte_buffer", ":checks", ":ip_address", + ":location", + ":logging", + ":macromagic", ":rtc_base", + ":rtc_event", ":socket", ":socket_address", ":socket_factory", ":socket_server", + ":stringutils", ":threading", + ":timeutils", "../api/units:time_delta", "../api/units:timestamp", + "../test:scoped_key_value_config", + "memory:always_valid_pointer", "memory:fifo_buffer", "synchronization:mutex", "task_utils:to_queued_task", @@ -1233,6 +1386,7 @@ rtc_library("rtc_base_tests_utils") { absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", ] } @@ -1245,7 +1399,8 @@ rtc_library("task_queue_for_test") { ] deps = [ ":checks", - ":rtc_base_approved", + ":location", + ":macromagic", ":rtc_event", ":rtc_task_queue", "../api/task_queue", @@ -1285,7 +1440,6 @@ if (rtc_include_tests) { deps = [ ":gunit_helpers", ":rtc_base", - ":rtc_base_approved", ":rtc_event", ":rtc_operations_chain", ":threading", @@ -1307,11 +1461,16 @@ if (rtc_include_tests) { "socket_unittest.h", ] deps = [ + ":buffer", ":checks", ":gunit_helpers", ":ip_address", + ":location", + ":logging", + ":macromagic", ":net_helpers", ":null_socket_server", + ":platform_thread", ":rtc_base", ":rtc_base_tests_utils", ":socket", @@ -1319,6 +1478,7 @@ if (rtc_include_tests) { ":socket_server", ":testclient", ":threading", + ":timeutils", "../system_wrappers", "../test:fileutils", "../test:test_main", @@ -1326,7 +1486,10 @@ if (rtc_include_tests) { "third_party/sigslot", "//testing/gtest", ] - absl_deps = [ "//third_party/abseil-cpp/absl/memory" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] } rtc_library("rtc_base_approved_unittests") { @@ -1375,33 +1538,56 @@ if (rtc_include_tests) { "virtual_socket_unittest.cc", "zero_memory_unittest.cc", ] - if (is_win) { - sources += [ "win/windows_version_unittest.cc" ] - } deps = [ + ":atomicops", + ":bit_buffer", ":bitstream_reader", ":bounded_inline_vector", + ":buffer", + ":buffer_queue", + ":byte_buffer", + ":byte_order", ":checks", + ":copy_on_write_buffer", ":criticalsection", ":divide_round", + ":event_tracer", ":gunit_helpers", + ":histogram_percentile_counter", ":ip_address", + ":location", + ":logging", + ":macromagic", + ":mod_ops", + ":moving_max_counter", ":null_socket_server", + ":one_time_event", + ":platform_thread", + ":random", ":rate_limiter", + ":rate_statistics", + ":rate_tracker", + ":refcount", ":rtc_base", - ":rtc_base_approved", ":rtc_base_tests_utils", + ":rtc_event", ":rtc_numerics", ":rtc_task_queue", ":safe_compare", ":safe_minmax", + ":sample_counter", ":sanitizer", ":socket", ":socket_address", ":socket_server", ":stringutils", + ":strong_alias", + ":swap_queue", ":testclient", ":threading", + ":timestamp_aligner", + ":timeutils", + ":zero_memory", "../api:array_view", "../api:scoped_refptr", "../api/numerics", @@ -1422,8 +1608,13 @@ if (rtc_include_tests) { "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/numeric:bits", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] + + if (is_win) { + deps += [ "win:windows_version_unittest" ] + } } rtc_library("rtc_task_queue_unittests") { @@ -1432,10 +1623,11 @@ if (rtc_include_tests) { sources = [ "task_queue_unittest.cc" ] deps = [ ":gunit_helpers", - ":rtc_base_approved", ":rtc_base_tests_utils", + ":rtc_event", ":rtc_task_queue", ":task_queue_for_test", + ":timeutils", "../test:test_main", "../test:test_support", ] @@ -1448,7 +1640,6 @@ if (rtc_include_tests) { sources = [ "weak_ptr_unittest.cc" ] deps = [ ":gunit_helpers", - ":rtc_base_approved", ":rtc_base_tests_utils", ":rtc_event", ":task_queue_for_test", @@ -1471,7 +1662,6 @@ if (rtc_include_tests) { "numerics/sequence_number_util_unittest.cc", ] deps = [ - ":rtc_base_approved", ":rtc_numerics", "../test:test_main", "../test:test_support", @@ -1517,12 +1707,21 @@ if (rtc_include_tests) { "unique_id_generator_unittest.cc", ] deps = [ + ":atomicops", + ":buffer", + ":buffer_queue", ":checks", ":gunit_helpers", ":ip_address", + ":logging", + ":macromagic", ":net_helpers", ":null_socket_server", + ":refcount", + ":rtc_base", ":rtc_base_tests_utils", + ":rtc_event", + ":safe_conversions", ":socket", ":socket_address", ":socket_factory", @@ -1530,12 +1729,14 @@ if (rtc_include_tests) { ":stringutils", ":testclient", ":threading", + ":timeutils", "../api:array_view", "../api/task_queue", "../api/task_queue:task_queue_test", "../test:field_trial", "../test:fileutils", "../test:rtc_expect_death", + "../test:scoped_key_value_config", "../test:test_main", "../test:test_support", "memory:fifo_buffer", @@ -1570,7 +1771,7 @@ if (rtc_include_tests) { "//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" ] } diff --git a/rtc_base/async_packet_socket.cc b/rtc_base/async_packet_socket.cc index d5435d71d0..1ce0d3b122 100644 --- a/rtc_base/async_packet_socket.cc +++ b/rtc_base/async_packet_socket.cc @@ -24,10 +24,24 @@ PacketOptions::PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {} PacketOptions::PacketOptions(const PacketOptions& other) = default; PacketOptions::~PacketOptions() = default; -AsyncPacketSocket::AsyncPacketSocket() = default; +AsyncPacketSocket::AsyncPacketSocket() { + network_checker_.Detach(); +} AsyncPacketSocket::~AsyncPacketSocket() = default; +void AsyncPacketSocket::SubscribeClose( + const void* removal_tag, + std::function callback) { + RTC_DCHECK_RUN_ON(&network_checker_); + on_close_.AddReceiver(removal_tag, std::move(callback)); +} + +void AsyncPacketSocket::UnsubscribeClose(const void* removal_tag) { + RTC_DCHECK_RUN_ON(&network_checker_); + on_close_.RemoveReceivers(removal_tag); +} + void CopySocketInformationToPacketInfo(size_t packet_size_bytes, const AsyncPacketSocket& socket_from, bool is_connectionless, diff --git a/rtc_base/async_packet_socket.h b/rtc_base/async_packet_socket.h index 2e334ec36d..aa31e25eab 100644 --- a/rtc_base/async_packet_socket.h +++ b/rtc_base/async_packet_socket.h @@ -13,9 +13,12 @@ #include +#include "api/sequence_checker.h" +#include "rtc_base/callback_list.h" #include "rtc_base/dscp.h" #include "rtc_base/network/sent_packet.h" #include "rtc_base/socket.h" +#include "rtc_base/system/no_unique_address.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/time_utils.h" @@ -100,6 +103,11 @@ class RTC_EXPORT AsyncPacketSocket : public sigslot::has_slots<> { virtual int GetError() const = 0; virtual void SetError(int error) = 0; + // Register a callback to be called when the socket is closed. + void SubscribeClose(const void* removal_tag, + std::function callback); + void UnsubscribeClose(const void* removal_tag); + // Emitted each time a packet is read. Used only for UDP and // connected TCP sockets. sigslot::signal5 { // CONNECTING to CONNECTED. sigslot::signal1 SignalConnect; - // Emitted for client TCP sockets when state is changed from - // CONNECTED to CLOSED. - sigslot::signal2 SignalClose; + void NotifyClosedForTest(int err) { NotifyClosed(err); } + + protected: + // TODO(bugs.webrtc.org/11943): Remove after updating downstream code. + void SignalClose(AsyncPacketSocket* s, int err) { + RTC_DCHECK_EQ(s, this); + NotifyClosed(err); + } + + void NotifyClosed(int err) { + RTC_DCHECK_RUN_ON(&network_checker_); + on_close_.Send(this, err); + } + + RTC_NO_UNIQUE_ADDRESS webrtc::SequenceChecker network_checker_; + + private: + webrtc::CallbackList on_close_ + RTC_GUARDED_BY(&network_checker_); }; // Listen socket, producing an AsyncPacketSocket when a peer connects. diff --git a/rtc_base/async_resolver.cc b/rtc_base/async_resolver.cc index ad1598f214..07d77b408f 100644 --- a/rtc_base/async_resolver.cc +++ b/rtc_base/async_resolver.cc @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/ref_counted_base.h" #include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_annotations.h" @@ -66,7 +67,7 @@ void PostTaskToGlobalQueue(std::unique_ptr task) { } // namespace #endif -int ResolveHostname(const std::string& hostname, +int ResolveHostname(absl::string_view hostname, int family, std::vector* addresses) { #ifdef __native_client__ @@ -99,7 +100,8 @@ int ResolveHostname(const std::string& hostname, // 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); + int ret = + getaddrinfo(std::string(hostname).c_str(), nullptr, &hints, &result); if (ret != 0) { return ret; } @@ -151,8 +153,7 @@ void AsyncResolver::Start(const SocketAddress& addr) { [this, addr, caller_task_queue = webrtc::TaskQueueBase::Current(), state = state_] { std::vector addresses; - int error = - ResolveHostname(addr.hostname().c_str(), addr.family(), &addresses); + int error = ResolveHostname(addr.hostname(), addr.family(), &addresses); webrtc::MutexLock lock(&state->mutex); if (state->status == State::Status::kLive) { caller_task_queue->PostTask(webrtc::ToQueuedTask( diff --git a/rtc_base/async_tcp_socket.cc b/rtc_base/async_tcp_socket.cc index c2480755b4..d29eafddb9 100644 --- a/rtc_base/async_tcp_socket.cc +++ b/rtc_base/async_tcp_socket.cc @@ -68,7 +68,6 @@ AsyncTCPSocketBase::AsyncTCPSocketBase(Socket* socket, max_outsize_(max_packet_size) { inbuf_.EnsureCapacity(kMinimumRecvSize); - RTC_DCHECK(socket_.get() != nullptr); socket_->SignalConnectEvent.connect(this, &AsyncTCPSocketBase::OnConnectEvent); socket_->SignalReadEvent.connect(this, &AsyncTCPSocketBase::OnReadEvent); @@ -237,7 +236,7 @@ void AsyncTCPSocketBase::OnWriteEvent(Socket* socket) { } void AsyncTCPSocketBase::OnCloseEvent(Socket* socket, int error) { - SignalClose(this, error); + NotifyClosed(error); } // AsyncTCPSocket diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc index 99b2ab3e24..a866224496 100644 --- a/rtc_base/boringssl_certificate.cc +++ b/rtc_base/boringssl_certificate.cc @@ -10,6 +10,8 @@ #include "rtc_base/boringssl_certificate.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -116,7 +118,7 @@ bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) { } // Adds an X.509 Common Name to `cbb`. -bool AddCommonName(CBB* cbb, const std::string& common_name) { +bool AddCommonName(CBB* cbb, absl::string_view common_name) { // See RFC 4519. static const uint8_t kCommonName[] = {0x55, 0x04, 0x03}; @@ -138,7 +140,7 @@ bool AddCommonName(CBB* cbb, const std::string& common_name) { !CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)) || !CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING) || !CBB_add_bytes(&value, - reinterpret_cast(common_name.c_str()), + reinterpret_cast(common_name.data()), common_name.size()) || !CBB_flush(cbb)) { return false; @@ -275,7 +277,7 @@ std::unique_ptr BoringSSLCertificate::Generate( } std::unique_ptr BoringSSLCertificate::FromPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { std::string der; if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) { return nullptr; @@ -340,7 +342,7 @@ bool BoringSSLCertificate::GetSignatureDigestAlgorithm( return false; } -bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm, +bool BoringSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { @@ -348,7 +350,7 @@ bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm, } bool BoringSSLCertificate::ComputeDigest(const CRYPTO_BUFFER* cert_buffer, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) { diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h index 8b4577a17c..bd331686b7 100644 --- a/rtc_base/boringssl_certificate.h +++ b/rtc_base/boringssl_certificate.h @@ -18,6 +18,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -38,7 +39,7 @@ class BoringSSLCertificate final : public SSLCertificate { OpenSSLKeyPair* key_pair, const SSLIdentityParams& params); static std::unique_ptr FromPEMString( - const std::string& pem_string); + absl::string_view pem_string); ~BoringSSLCertificate() override; @@ -55,14 +56,14 @@ class BoringSSLCertificate final : public SSLCertificate { bool operator!=(const BoringSSLCertificate& other) const; // Compute the digest of the certificate given `algorithm`. - bool ComputeDigest(const std::string& algorithm, + bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const override; // Compute the digest of a certificate as a CRYPTO_BUFFER. static bool ComputeDigest(const CRYPTO_BUFFER* cert_buffer, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length); diff --git a/rtc_base/boringssl_identity.cc b/rtc_base/boringssl_identity.cc index d22c8ce529..a61524a679 100644 --- a/rtc_base/boringssl_identity.cc +++ b/rtc_base/boringssl_identity.cc @@ -22,6 +22,7 @@ #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_conversions.h" @@ -67,12 +68,12 @@ std::unique_ptr BoringSSLIdentity::CreateInternal( // static std::unique_ptr BoringSSLIdentity::CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime) { SSLIdentityParams params; params.key_params = key_params; - params.common_name = common_name; + params.common_name = std::string(common_name); time_t now = time(nullptr); params.not_before = now + kCertificateWindowInSeconds; params.not_after = now + certificate_lifetime; @@ -87,8 +88,8 @@ std::unique_ptr BoringSSLIdentity::CreateForTest( } std::unique_ptr BoringSSLIdentity::CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate) { + absl::string_view private_key, + absl::string_view certificate) { std::unique_ptr cert( BoringSSLCertificate::FromPEMString(certificate)); if (!cert) { @@ -108,8 +109,8 @@ std::unique_ptr BoringSSLIdentity::CreateFromPEMStrings( } std::unique_ptr BoringSSLIdentity::CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain) { + absl::string_view private_key, + absl::string_view certificate_chain) { bssl::UniquePtr bio( BIO_new_mem_buf(certificate_chain.data(), rtc::dchecked_cast(certificate_chain.size()))); diff --git a/rtc_base/boringssl_identity.h b/rtc_base/boringssl_identity.h index e322afaba0..ffc8812af2 100644 --- a/rtc_base/boringssl_identity.h +++ b/rtc_base/boringssl_identity.h @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/boringssl_certificate.h" #include "rtc_base/openssl_key_pair.h" #include "rtc_base/ssl_certificate.h" @@ -30,17 +31,17 @@ namespace rtc { class BoringSSLIdentity final : public SSLIdentity { public: static std::unique_ptr CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime); static std::unique_ptr CreateForTest( const SSLIdentityParams& params); static std::unique_ptr CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate); + absl::string_view private_key, + absl::string_view certificate); static std::unique_ptr CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain); + absl::string_view private_key, + absl::string_view certificate_chain); ~BoringSSLIdentity() override; BoringSSLIdentity(const BoringSSLIdentity&) = delete; diff --git a/rtc_base/buffer.h b/rtc_base/buffer.h index c9bf2ccebf..be8e22bf9f 100644 --- a/rtc_base/buffer.h +++ b/rtc_base/buffer.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/type_traits.h" @@ -117,6 +118,13 @@ class BufferT { ~BufferT() { MaybeZeroCompleteBuffer(); } + // Implicit conversion to absl::string_view if T is compatible with char. + template + operator typename std::enable_if::value, + absl::string_view>::type() const { + return absl::string_view(data(), size()); + } + // Get a pointer to the data. Just .data() will give you a (const) T*, but if // T is a byte-sized integer, you may also use .data() for any other // byte-sized integer U. diff --git a/rtc_base/buffer_unittest.cc b/rtc_base/buffer_unittest.cc index 8beae43cf9..b56118afde 100644 --- a/rtc_base/buffer_unittest.cc +++ b/rtc_base/buffer_unittest.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "test/gmock.h" #include "test/gtest.h" @@ -73,6 +74,13 @@ TEST(BufferTest, TestConstructArray) { EXPECT_EQ(0, memcmp(buf.data(), kTestData, 16)); } +TEST(BufferTest, TestStringViewConversion) { + Buffer buf(kTestData); + absl::string_view view = buf; + EXPECT_EQ(view, + absl::string_view(reinterpret_cast(kTestData), 16u)); +} + TEST(BufferTest, TestSetData) { Buffer buf(kTestData + 4, 7); buf.SetData(kTestData, 9); diff --git a/rtc_base/byte_buffer.h b/rtc_base/byte_buffer.h index d2dda3c8e1..9bcbb838aa 100644 --- a/rtc_base/byte_buffer.h +++ b/rtc_base/byte_buffer.h @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_order.h" @@ -72,8 +73,8 @@ class ByteBufferWriterT { char last_byte = static_cast(val); WriteBytes(&last_byte, 1); } - void WriteString(const std::string& val) { - WriteBytes(val.c_str(), val.size()); + void WriteString(absl::string_view val) { + WriteBytes(val.data(), val.size()); } void WriteBytes(const char* val, size_t len) { buffer_.AppendData(val, len); } diff --git a/rtc_base/callback_list.cc b/rtc_base/callback_list.cc index 88d0b6fc71..c452c79b38 100644 --- a/rtc_base/callback_list.cc +++ b/rtc_base/callback_list.cc @@ -22,8 +22,7 @@ CallbackListReceivers::~CallbackListReceivers() { } void CallbackListReceivers::RemoveReceivers(const void* removal_tag) { - RTC_CHECK(!send_in_progress_); - RTC_DCHECK(removal_tag != nullptr); + RTC_DCHECK(removal_tag); // We divide the receivers_ vector into three regions: from right to left, the // "keep" region, the "todo" region, and the "remove" region. The "todo" @@ -42,8 +41,13 @@ void CallbackListReceivers::RemoveReceivers(const void* removal_tag) { } else if (receivers_[first_remove - 1].removal_tag == removal_tag) { // The last element of the "todo" region should be removed. Move the // "todo"/"remove" boundary. + if (send_in_progress_) { + // Tag this receiver for removal, which will be done when `ForEach` + // has completed. + receivers_[first_remove - 1].removal_tag = pending_removal_tag(); + } --first_remove; - } else { + } else if (!send_in_progress_) { // The first element of the "todo" region should be removed, and the last // element of the "todo" region should be kept. Swap them, and then shrink // the "todo" region from both ends. @@ -57,18 +61,28 @@ void CallbackListReceivers::RemoveReceivers(const void* removal_tag) { } } - // Discard the remove region. - receivers_.resize(first_remove); + if (!send_in_progress_) { + // Discard the remove region. + receivers_.resize(first_remove); + } } void CallbackListReceivers::Foreach( rtc::FunctionView fv) { RTC_CHECK(!send_in_progress_); + bool removals_detected = false; send_in_progress_ = true; for (auto& r : receivers_) { + RTC_DCHECK_NE(r.removal_tag, pending_removal_tag()); fv(r.function); + if (r.removal_tag == pending_removal_tag()) { + removals_detected = true; + } } send_in_progress_ = false; + if (removals_detected) { + RemoveReceivers(pending_removal_tag()); + } } template void CallbackListReceivers::AddReceiver( diff --git a/rtc_base/callback_list.h b/rtc_base/callback_list.h index 18d48b02ee..a9d71a6562 100644 --- a/rtc_base/callback_list.h +++ b/rtc_base/callback_list.h @@ -18,12 +18,13 @@ #include "rtc_base/checks.h" #include "rtc_base/system/assume.h" #include "rtc_base/system/inline.h" +#include "rtc_base/system/rtc_export.h" #include "rtc_base/untyped_function.h" namespace webrtc { namespace callback_list_impl { -class CallbackListReceivers { +class RTC_EXPORT CallbackListReceivers { public: CallbackListReceivers(); CallbackListReceivers(const CallbackListReceivers&) = delete; @@ -51,10 +52,18 @@ class CallbackListReceivers { void Foreach(rtc::FunctionView fv); private: + // Special protected pointer value that's used as a removal_tag for + // receivers that want to unsubscribe from within a callback. + // Note we could use `&receivers_` too, but since it's the first member + // variable of the class, its address will be the same as the instance + // CallbackList instance, so we take an extra step to avoid collision. + const void* pending_removal_tag() const { return &send_in_progress_; } + struct Callback { const void* removal_tag; UntypedFunction function; }; + std::vector receivers_; bool send_in_progress_ = false; }; diff --git a/rtc_base/callback_list_unittest.cc b/rtc_base/callback_list_unittest.cc index 23dfff0bdd..483eb3f99a 100644 --- a/rtc_base/callback_list_unittest.cc +++ b/rtc_base/callback_list_unittest.cc @@ -17,7 +17,7 @@ namespace webrtc { namespace { -TEST(CallbackList, NoRecieverSingleMessageTest) { +TEST(CallbackList, NoReceiverSingleMessageTest) { CallbackList c; c.Send("message"); @@ -252,5 +252,18 @@ TEST(CallbackList, RemoveManyReceivers) { EXPECT_EQ(accumulator, 1212); } +TEST(CallbackList, RemoveFromSend) { + int removal_tag = 0; + CallbackList<> c; + c.AddReceiver(&removal_tag, [&] { + c.RemoveReceivers(&removal_tag); + // Do after RemoveReceivers to make sure the lambda is still valid. + ++removal_tag; + }); + c.Send(); + c.Send(); + EXPECT_EQ(removal_tag, 1); +} + } // namespace } // namespace webrtc diff --git a/rtc_base/checks.cc b/rtc_base/checks.cc index 239ea9f0da..d6974d722f 100644 --- a/rtc_base/checks.cc +++ b/rtc_base/checks.cc @@ -15,6 +15,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_ANDROID) #define RTC_LOG_TAG_ANDROID "rtc" #include // NOLINT @@ -37,13 +39,14 @@ namespace { -RTC_NORETURN void WriteFatalLogAndAbort(const std::string& output) { - const char* output_c = output.c_str(); +RTC_NORETURN void WriteFatalLogAndAbort(absl::string_view output) { #if defined(WEBRTC_ANDROID) - __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", output_c); + std::string output_str = std::string(output); + __android_log_print(ANDROID_LOG_ERROR, RTC_LOG_TAG_ANDROID, "%s\n", + output_str.c_str()); #endif fflush(stdout); - fprintf(stderr, "%s", output_c); + fwrite(output.data(), output.size(), 1, stderr); fflush(stderr); #if defined(WEBRTC_WIN) DebugBreak(); diff --git a/rtc_base/checks.h b/rtc_base/checks.h index 863e39d651..6ffa30da59 100644 --- a/rtc_base/checks.h +++ b/rtc_base/checks.h @@ -56,6 +56,7 @@ RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); #include "absl/meta/type_traits.h" #include "absl/strings/string_view.h" +#include "api/scoped_refptr.h" #include "rtc_base/numerics/safe_compare.h" #include "rtc_base/system/inline.h" #include "rtc_base/system/rtc_export.h" @@ -192,6 +193,12 @@ inline Val MakeVal(const void* x) { return {x}; } +template +inline Val MakeVal( + const rtc::scoped_refptr& p) { + return {p.get()}; +} + // The enum class types are not implicitly convertible to arithmetic types. template ::value && diff --git a/rtc_base/copy_on_write_buffer.cc b/rtc_base/copy_on_write_buffer.cc index f3cc710f85..850327b088 100644 --- a/rtc_base/copy_on_write_buffer.cc +++ b/rtc_base/copy_on_write_buffer.cc @@ -12,6 +12,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { CopyOnWriteBuffer::CopyOnWriteBuffer() : offset_(0), size_(0) { @@ -28,7 +30,7 @@ CopyOnWriteBuffer::CopyOnWriteBuffer(CopyOnWriteBuffer&& buf) RTC_DCHECK(IsConsistent()); } -CopyOnWriteBuffer::CopyOnWriteBuffer(const std::string& s) +CopyOnWriteBuffer::CopyOnWriteBuffer(absl::string_view s) : CopyOnWriteBuffer(s.data(), s.length()) {} CopyOnWriteBuffer::CopyOnWriteBuffer(size_t size) diff --git a/rtc_base/copy_on_write_buffer.h b/rtc_base/copy_on_write_buffer.h index 6837f06526..849f5f5df2 100644 --- a/rtc_base/copy_on_write_buffer.h +++ b/rtc_base/copy_on_write_buffer.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/scoped_refptr.h" #include "rtc_base/buffer.h" #include "rtc_base/checks.h" @@ -38,7 +39,7 @@ class RTC_EXPORT CopyOnWriteBuffer { CopyOnWriteBuffer(CopyOnWriteBuffer&& buf); // Construct a buffer from a string, convenient for unittests. - CopyOnWriteBuffer(const std::string& s); + explicit CopyOnWriteBuffer(absl::string_view s); // Construct a buffer with the specified number of uninitialized bytes. explicit CopyOnWriteBuffer(size_t size); diff --git a/rtc_base/crc32.h b/rtc_base/crc32.h index ca8578d69c..93376a5a12 100644 --- a/rtc_base/crc32.h +++ b/rtc_base/crc32.h @@ -16,6 +16,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // Updates a CRC32 checksum with `len` bytes from `buf`. `initial` holds the @@ -26,8 +28,8 @@ uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len); inline uint32_t ComputeCrc32(const void* buf, size_t len) { return UpdateCrc32(0, buf, len); } -inline uint32_t ComputeCrc32(const std::string& str) { - return ComputeCrc32(str.c_str(), str.size()); +inline uint32_t ComputeCrc32(absl::string_view str) { + return ComputeCrc32(str.data(), str.size()); } } // namespace rtc diff --git a/rtc_base/event_tracer.cc b/rtc_base/event_tracer.cc index 1a2b41ec5c..e14079e07c 100644 --- a/rtc_base/event_tracer.cc +++ b/rtc_base/event_tracer.cc @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/sequence_checker.h" #include "rtc_base/atomic_ops.h" #include "rtc_base/checks.h" @@ -367,11 +368,11 @@ void StartInternalCaptureToFile(FILE* file) { } } -bool StartInternalCapture(const char* filename) { +bool StartInternalCapture(absl::string_view filename) { if (!g_event_logger) return false; - FILE* file = fopen(filename, "w"); + FILE* file = fopen(std::string(filename).c_str(), "w"); if (!file) { RTC_LOG(LS_ERROR) << "Failed to open trace file '" << filename << "' for writing."; diff --git a/rtc_base/event_tracer.h b/rtc_base/event_tracer.h index 68aaf0d8ab..77c2b35597 100644 --- a/rtc_base/event_tracer.h +++ b/rtc_base/event_tracer.h @@ -28,6 +28,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -73,7 +74,7 @@ namespace rtc { namespace tracing { // Set up internal event tracer. RTC_EXPORT void SetupInternalTracer(); -RTC_EXPORT bool StartInternalCapture(const char* filename); +RTC_EXPORT bool StartInternalCapture(absl::string_view filename); RTC_EXPORT void StartInternalCaptureToFile(FILE* file); RTC_EXPORT void StopInternalCapture(); // Make sure we run this, this will tear down the internal tracing. diff --git a/rtc_base/experiments/BUILD.gn b/rtc_base/experiments/BUILD.gn index 56d5000869..213377218e 100644 --- a/rtc_base/experiments/BUILD.gn +++ b/rtc_base/experiments/BUILD.gn @@ -14,11 +14,14 @@ rtc_library("alr_experiment") { "alr_experiment.h", ] deps = [ - "../:rtc_base_approved", + "..:logging", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings:strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("field_trial_parser") { @@ -55,9 +58,9 @@ rtc_library("quality_rampup_experiment") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -70,9 +73,9 @@ rtc_library("quality_scaler_settings") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -85,9 +88,9 @@ rtc_library("bandwidth_quality_scaler_settings") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -99,7 +102,7 @@ rtc_library("quality_scaling_experiment") { "quality_scaling_experiment.h", ] deps = [ - "../:rtc_base_approved", + "..:logging", "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", ] @@ -112,7 +115,7 @@ rtc_library("normalize_simulcast_size_experiment") { "normalize_simulcast_size_experiment.h", ] deps = [ - "../:rtc_base_approved", + "..:logging", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -125,7 +128,8 @@ rtc_library("balanced_degradation_settings") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", + "../../api:field_trials_view", "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", ] @@ -139,7 +143,7 @@ rtc_library("cpu_speed_experiment") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -152,11 +156,14 @@ rtc_library("encoder_info_settings") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("rtt_mult_experiment") { @@ -165,7 +172,7 @@ rtc_library("rtt_mult_experiment") { "rtt_mult_experiment.h", ] deps = [ - "../:rtc_base_approved", + "..:logging", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -177,7 +184,7 @@ rtc_library("jitter_upper_bound_experiment") { "jitter_upper_bound_experiment.h", ] deps = [ - "../:rtc_base_approved", + "..:logging", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -190,9 +197,10 @@ rtc_library("rate_control_settings") { ] deps = [ ":field_trial_parser", - "../:rtc_base_approved", + "..:logging", + "..:safe_conversions", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", "../../api/units:data_size", "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", @@ -210,8 +218,8 @@ rtc_library("keyframe_interval_settings_experiment") { ] deps = [ ":field_trial_parser", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -224,8 +232,8 @@ rtc_library("stable_target_rate_experiment") { deps = [ ":field_trial_parser", ":rate_control_settings", + "../../api:field_trials_view", "../../api/transport:field_trial_based_config", - "../../api/transport:webrtc_key_value_config", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -291,9 +299,13 @@ if (rtc_include_tests && !build_with_chromium) { "../../api/video_codecs:video_codecs_api", "../../system_wrappers:field_trial", "../../test:field_trial", + "../../test:scoped_key_value_config", "../../test:test_main", "../../test:test_support", ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } } diff --git a/rtc_base/experiments/alr_experiment.cc b/rtc_base/experiments/alr_experiment.cc index 119a4011e1..f5d36f6867 100644 --- a/rtc_base/experiments/alr_experiment.cc +++ b/rtc_base/experiments/alr_experiment.cc @@ -15,6 +15,7 @@ #include +#include "absl/strings/string_view.h" #include "api/transport/field_trial_based_config.h" #include "rtc_base/logging.h" @@ -32,22 +33,22 @@ bool AlrExperimentSettings::MaxOneFieldTrialEnabled() { } bool AlrExperimentSettings::MaxOneFieldTrialEnabled( - const WebRtcKeyValueConfig& key_value_config) { + const FieldTrialsView& key_value_config) { return key_value_config.Lookup(kStrictPacingAndProbingExperimentName) .empty() || key_value_config.Lookup(kScreenshareProbingBweExperimentName).empty(); } absl::optional -AlrExperimentSettings::CreateFromFieldTrial(const char* experiment_name) { +AlrExperimentSettings::CreateFromFieldTrial(absl::string_view experiment_name) { return AlrExperimentSettings::CreateFromFieldTrial(FieldTrialBasedConfig(), experiment_name); } absl::optional AlrExperimentSettings::CreateFromFieldTrial( - const WebRtcKeyValueConfig& key_value_config, - const char* experiment_name) { + const FieldTrialsView& key_value_config, + absl::string_view experiment_name) { absl::optional ret; std::string group_name = key_value_config.Lookup(experiment_name); diff --git a/rtc_base/experiments/alr_experiment.h b/rtc_base/experiments/alr_experiment.h index 5b0661c5b4..048fd90cab 100644 --- a/rtc_base/experiments/alr_experiment.h +++ b/rtc_base/experiments/alr_experiment.h @@ -13,8 +13,9 @@ #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" namespace webrtc { struct AlrExperimentSettings { @@ -32,13 +33,12 @@ struct AlrExperimentSettings { static const char kScreenshareProbingBweExperimentName[]; static const char kStrictPacingAndProbingExperimentName[]; static absl::optional CreateFromFieldTrial( - const char* experiment_name); + absl::string_view experiment_name); static absl::optional CreateFromFieldTrial( - const WebRtcKeyValueConfig& key_value_config, - const char* experiment_name); + const FieldTrialsView& key_value_config, + absl::string_view experiment_name); static bool MaxOneFieldTrialEnabled(); - static bool MaxOneFieldTrialEnabled( - const WebRtcKeyValueConfig& key_value_config); + static bool MaxOneFieldTrialEnabled(const FieldTrialsView& key_value_config); private: AlrExperimentSettings() = default; diff --git a/rtc_base/experiments/balanced_degradation_settings.cc b/rtc_base/experiments/balanced_degradation_settings.cc index 90d44efb10..1652e31704 100644 --- a/rtc_base/experiments/balanced_degradation_settings.cc +++ b/rtc_base/experiments/balanced_degradation_settings.cc @@ -15,7 +15,6 @@ #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/logging.h" -#include "system_wrappers/include/field_trial.h" namespace webrtc { namespace { @@ -332,7 +331,8 @@ BalancedDegradationSettings::Config::Config(int pixels, av1(av1), generic(generic) {} -BalancedDegradationSettings::BalancedDegradationSettings() { +BalancedDegradationSettings::BalancedDegradationSettings( + const FieldTrialsView& field_trials) { FieldTrialStructList configs( {FieldTrialStructMember("pixels", [](Config* c) { return &c->pixels; }), FieldTrialStructMember("fps", [](Config* c) { return &c->fps; }), @@ -390,7 +390,7 @@ BalancedDegradationSettings::BalancedDegradationSettings() { [](Config* c) { return &c->generic.kbps_res; })}, {}); - ParseFieldTrial({&configs}, field_trial::FindFullName(kFieldTrial)); + ParseFieldTrial({&configs}, field_trials.Lookup(kFieldTrial)); configs_ = GetValidOrDefault(configs.Get()); RTC_DCHECK_GT(configs_.size(), 1); diff --git a/rtc_base/experiments/balanced_degradation_settings.h b/rtc_base/experiments/balanced_degradation_settings.h index 0b2f2f5993..0b5e03df3b 100644 --- a/rtc_base/experiments/balanced_degradation_settings.h +++ b/rtc_base/experiments/balanced_degradation_settings.h @@ -14,6 +14,7 @@ #include #include "absl/types/optional.h" +#include "api/field_trials_view.h" #include "api/video_codecs/video_encoder.h" namespace webrtc { @@ -22,7 +23,7 @@ class BalancedDegradationSettings { public: static constexpr int kNoFpsDiff = -100; - BalancedDegradationSettings(); + BalancedDegradationSettings(const FieldTrialsView& field_trials); ~BalancedDegradationSettings(); struct CodecTypeSpecific { diff --git a/rtc_base/experiments/balanced_degradation_settings_unittest.cc b/rtc_base/experiments/balanced_degradation_settings_unittest.cc index 92833ee98c..a32dbb4aaa 100644 --- a/rtc_base/experiments/balanced_degradation_settings_unittest.cc +++ b/rtc_base/experiments/balanced_degradation_settings_unittest.cc @@ -13,8 +13,8 @@ #include #include "rtc_base/gunit.h" -#include "test/field_trial.h" #include "test/gmock.h" +#include "test/scoped_key_value_config.h" namespace webrtc { namespace { @@ -59,8 +59,8 @@ void VerifyIsDefault( } // namespace TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) { - webrtc::test::ScopedFieldTrials field_trials(""); - BalancedDegradationSettings settings; + webrtc::test::ScopedKeyValueConfig field_trials(""); + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); EXPECT_TRUE(settings.CanAdaptUp(kVideoCodecVP8, 1, /*bitrate_bps*/ 1)); EXPECT_TRUE( @@ -75,10 +75,10 @@ TEST(BalancedDegradationSettings, GetsDefaultConfigIfNoList) { } TEST(BalancedDegradationSettings, GetsConfig) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,other:4|5|6/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -117,35 +117,35 @@ TEST(BalancedDegradationSettings, GetsConfig) { } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroFpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:0|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfPixelsDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|999|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfFramerateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|4|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|8|9,vp9_fps:9|10|11," "h264_fps:11|12|13,av1_fps:1|2|3,generic_fps:13|14|15/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -184,34 +184,34 @@ TEST(BalancedDegradationSettings, GetsConfigWithSpecificFps) { } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroVp8FpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:0|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigForInvalidFpsValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:7|15|25,vp8_fps:10|15|2000/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfVp8FramerateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:4|5|25,vp8_fps:5|4|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsMinFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1)); EXPECT_EQ(5, settings.MinFps(kVideoCodecVP8, 1000)); EXPECT_EQ(15, settings.MinFps(kVideoCodecVP8, 1001)); @@ -223,10 +223,10 @@ TEST(BalancedDegradationSettings, GetsMinFps) { } TEST(BalancedDegradationSettings, GetsVp8MinFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1)); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP8, 1000)); EXPECT_EQ(10, settings.MinFps(kVideoCodecVP8, 1001)); @@ -238,10 +238,10 @@ TEST(BalancedDegradationSettings, GetsVp8MinFps) { } TEST(BalancedDegradationSettings, GetsMaxFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1)); EXPECT_EQ(15, settings.MaxFps(kVideoCodecVP8, 1000)); EXPECT_EQ(25, settings.MaxFps(kVideoCodecVP8, 1001)); @@ -251,10 +251,10 @@ TEST(BalancedDegradationSettings, GetsMaxFps) { } TEST(BalancedDegradationSettings, GetsVp8MaxFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1)); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP8, 1000)); EXPECT_EQ(12, settings.MaxFps(kVideoCodecVP8, 1001)); @@ -264,39 +264,39 @@ TEST(BalancedDegradationSettings, GetsVp8MaxFps) { } TEST(BalancedDegradationSettings, GetsVp9Fps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp9_fps:7|10|12/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(7, settings.MinFps(kVideoCodecVP9, 1000)); EXPECT_EQ(10, settings.MaxFps(kVideoCodecVP9, 1000)); } TEST(BalancedDegradationSettings, GetsH264Fps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,h264_fps:8|11|13/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(11, settings.MinFps(kVideoCodecH264, 2000)); EXPECT_EQ(13, settings.MaxFps(kVideoCodecH264, 2000)); } TEST(BalancedDegradationSettings, GetsGenericFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,generic_fps:9|12|14/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(14, settings.MinFps(kVideoCodecGeneric, 3000)); EXPECT_EQ(std::numeric_limits::max(), settings.MaxFps(kVideoCodecGeneric, 3000)); } TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|100,vp8_fps:30|100|100/"); const int kUnlimitedFps = std::numeric_limits::max(); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(15, settings.MinFps(kVideoCodecH264, 2000)); EXPECT_EQ(kUnlimitedFps, settings.MinFps(kVideoCodecH264, 2001)); EXPECT_EQ(30, settings.MinFps(kVideoCodecVP8, 1000)); @@ -304,7 +304,7 @@ TEST(BalancedDegradationSettings, GetsUnlimitedForMaxValidFps) { } TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|88|99,kbps_res:55|111|222," "vp8_kbps:11|12|13,vp8_kbps_res:14|15|16," @@ -312,7 +312,7 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { "h264_kbps:31|32|33,h264_kbps_res:34|35|36," "av1_kbps:41|42|43,av1_kbps_res:44|45|46," "generic_kbps:51|52|53,generic_kbps_res:54|55|56/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -351,29 +351,29 @@ TEST(BalancedDegradationSettings, GetsConfigWithBitrate) { } TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreases) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|43|99/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfBitrateDecreasesWithUnsetValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:11|22|33,fps:5|15|25,kbps:44|0|43/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, CanAdaptUp) { VideoCodecType vp8 = kVideoCodecVP8; - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,kbps:0|80|0|90," "vp9_kbps:40|50|60|70/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_TRUE(s.CanAdaptUp(vp8, 1000, 0)); // No bitrate provided. EXPECT_FALSE(s.CanAdaptUp(vp8, 1000, 79000)); EXPECT_TRUE(s.CanAdaptUp(vp8, 1000, 80000)); @@ -384,12 +384,12 @@ TEST(BalancedDegradationSettings, CanAdaptUp) { } TEST(BalancedDegradationSettings, CanAdaptUpWithCodecType) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,vp8_kbps:0|30|40|50," "vp9_kbps:0|60|70|80,h264_kbps:0|55|65|75,av1_kbps:0|77|88|99," "generic_kbps:0|25|35|45/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_FALSE(s.CanAdaptUp(kVideoCodecVP8, 1000, 29000)); EXPECT_TRUE(s.CanAdaptUp(kVideoCodecVP8, 1000, 30000)); EXPECT_FALSE(s.CanAdaptUp(kVideoCodecVP9, 1000, 59000)); @@ -405,11 +405,11 @@ TEST(BalancedDegradationSettings, CanAdaptUpWithCodecType) { TEST(BalancedDegradationSettings, CanAdaptUpResolution) { VideoCodecType vp8 = kVideoCodecVP8; - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,kbps_res:0|80|0|90," "vp9_kbps_res:40|50|60|70/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_TRUE(s.CanAdaptUpResolution(vp8, 1000, 0)); // No bitrate provided. EXPECT_FALSE(s.CanAdaptUpResolution(vp8, 1000, 79000)); EXPECT_TRUE(s.CanAdaptUpResolution(vp8, 1000, 80000)); @@ -420,12 +420,12 @@ TEST(BalancedDegradationSettings, CanAdaptUpResolution) { } TEST(BalancedDegradationSettings, CanAdaptUpResolutionWithCodecType) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000|4000,fps:5|15|25|30,vp8_kbps_res:0|30|40|50," "vp9_kbps_res:0|60|70|80,h264_kbps_res:0|55|65|75," "av1_kbps_res:0|77|88|99,generic_kbps_res:0|25|35|45/"); - BalancedDegradationSettings s; + BalancedDegradationSettings s(field_trials); EXPECT_FALSE(s.CanAdaptUpResolution(kVideoCodecVP8, 1000, 29000)); EXPECT_TRUE(s.CanAdaptUpResolution(kVideoCodecVP8, 1000, 30000)); EXPECT_FALSE(s.CanAdaptUpResolution(kVideoCodecVP9, 1000, 59000)); @@ -441,10 +441,10 @@ TEST(BalancedDegradationSettings, CanAdaptUpResolutionWithCodecType) { } TEST(BalancedDegradationSettings, GetsFpsDiff) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,fps_diff:0|-2|3/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(0, settings.MinFpsDiff(1)); EXPECT_EQ(0, settings.MinFpsDiff(1000)); EXPECT_EQ(-2, settings.MinFpsDiff(1001)); @@ -455,21 +455,21 @@ TEST(BalancedDegradationSettings, GetsFpsDiff) { } TEST(BalancedDegradationSettings, GetsNoFpsDiffIfValueBelowMinSetting) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,fps_diff:-100|-99|-101/"); // Min valid fps_diff setting: -99. - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_FALSE(settings.MinFpsDiff(1000)); EXPECT_EQ(-99, settings.MinFpsDiff(2000)); EXPECT_FALSE(settings.MinFpsDiff(3000)); } TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP8, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecVP9, 1)); EXPECT_FALSE(settings.GetQpThresholds(kVideoCodecH264, 1)); @@ -478,13 +478,13 @@ TEST(BalancedDegradationSettings, QpThresholdsNotSetByDefault) { } TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_low:89|90|88," "vp8_qp_high:90|91|92,vp9_qp_low:27|28|29,vp9_qp_high:120|130|140," "h264_qp_low:12|13|14,h264_qp_high:20|30|40,av1_qp_low:2|3|4," "av1_qp_high:11|33|44,generic_qp_low:7|6|5,generic_qp_high:22|23|24/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_THAT(settings.GetConfigs(), ::testing::ElementsAre( BalancedDegradationSettings::Config{ @@ -523,54 +523,54 @@ TEST(BalancedDegradationSettings, GetsConfigWithQpThresholds) { } TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasLowThreshold) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_low:89|90|88/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfOnlyHasHighThreshold) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfLowEqualsHigh) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|88/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigIfLowGreaterThanHigh) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|87/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsDefaultConfigForZeroQpValue) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|0|88,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); VerifyIsDefault(settings.GetConfigs()); } TEST(BalancedDegradationSettings, GetsVp8QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp8_qp_low:89|90|88,vp8_qp_high:90|91|92/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); EXPECT_EQ(89, settings.GetQpThresholds(kVideoCodecVP8, 1)->low); EXPECT_EQ(90, settings.GetQpThresholds(kVideoCodecVP8, 1)->high); EXPECT_EQ(90, settings.GetQpThresholds(kVideoCodecVP8, 1000)->high); @@ -582,11 +582,11 @@ TEST(BalancedDegradationSettings, GetsVp8QpThresholds) { } TEST(BalancedDegradationSettings, GetsVp9QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "vp9_qp_low:55|56|57,vp9_qp_high:155|156|157/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecVP9, 1000); EXPECT_TRUE(thresholds); EXPECT_EQ(55, thresholds->low); @@ -594,11 +594,11 @@ TEST(BalancedDegradationSettings, GetsVp9QpThresholds) { } TEST(BalancedDegradationSettings, GetsH264QpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "h264_qp_low:21|22|23,h264_qp_high:41|43|42/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecH264, 2000); EXPECT_TRUE(thresholds); EXPECT_EQ(22, thresholds->low); @@ -606,11 +606,11 @@ TEST(BalancedDegradationSettings, GetsH264QpThresholds) { } TEST(BalancedDegradationSettings, GetsGenericQpThresholds) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-Video-BalancedDegradationSettings/" "pixels:1000|2000|3000,fps:5|15|25," "generic_qp_low:2|3|4,generic_qp_high:22|23|24/"); - BalancedDegradationSettings settings; + BalancedDegradationSettings settings(field_trials); const auto thresholds = settings.GetQpThresholds(kVideoCodecGeneric, 3000); EXPECT_TRUE(thresholds); EXPECT_EQ(4, thresholds->low); diff --git a/rtc_base/experiments/bandwidth_quality_scaler_settings.cc b/rtc_base/experiments/bandwidth_quality_scaler_settings.cc index 332ab6be4b..0a9df493ed 100644 --- a/rtc_base/experiments/bandwidth_quality_scaler_settings.cc +++ b/rtc_base/experiments/bandwidth_quality_scaler_settings.cc @@ -16,7 +16,7 @@ namespace webrtc { BandwidthQualityScalerSettings::BandwidthQualityScalerSettings( - const WebRtcKeyValueConfig* const key_value_config) + const FieldTrialsView* const key_value_config) : bitrate_state_update_interval_s_("bitrate_state_update_interval_s_") { ParseFieldTrial( {&bitrate_state_update_interval_s_}, diff --git a/rtc_base/experiments/bandwidth_quality_scaler_settings.h b/rtc_base/experiments/bandwidth_quality_scaler_settings.h index 959aea5bd3..21e115df01 100644 --- a/rtc_base/experiments/bandwidth_quality_scaler_settings.h +++ b/rtc_base/experiments/bandwidth_quality_scaler_settings.h @@ -12,7 +12,7 @@ #define RTC_BASE_EXPERIMENTS_BANDWIDTH_QUALITY_SCALER_SETTINGS_H_ #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -25,7 +25,7 @@ class BandwidthQualityScalerSettings final { private: explicit BandwidthQualityScalerSettings( - const WebRtcKeyValueConfig* const key_value_config); + const FieldTrialsView* const key_value_config); FieldTrialOptional bitrate_state_update_interval_s_; }; diff --git a/rtc_base/experiments/encoder_info_settings.cc b/rtc_base/experiments/encoder_info_settings.cc index b39c68468f..a74eb50a43 100644 --- a/rtc_base/experiments/encoder_info_settings.cc +++ b/rtc_base/experiments/encoder_info_settings.cc @@ -12,6 +12,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/logging.h" #include "system_wrappers/include/field_trial.h" @@ -155,7 +156,7 @@ EncoderInfoSettings::GetSinglecastBitrateLimitForResolutionWhenQpIsUntrusted( } } -EncoderInfoSettings::EncoderInfoSettings(std::string name) +EncoderInfoSettings::EncoderInfoSettings(absl::string_view name) : requested_resolution_alignment_("requested_resolution_alignment"), apply_alignment_to_all_simulcast_layers_( "apply_alignment_to_all_simulcast_layers") { @@ -174,14 +175,15 @@ EncoderInfoSettings::EncoderInfoSettings(std::string name) [](BitrateLimit* b) { return &b->max_bitrate_bps; })}, {}); - if (field_trial::FindFullName(name).empty()) { + std::string name_str = std::string(name); + if (field_trial::FindFullName(name_str).empty()) { // Encoder name not found, use common string applying to all encoders. - name = "WebRTC-GetEncoderInfoOverride"; + name_str = "WebRTC-GetEncoderInfoOverride"; } ParseFieldTrial({&bitrate_limits, &requested_resolution_alignment_, &apply_alignment_to_all_simulcast_layers_}, - field_trial::FindFullName(name)); + field_trial::FindFullName(name_str)); resolution_bitrate_limits_ = ToResolutionBitrateLimits(bitrate_limits.Get()); } diff --git a/rtc_base/experiments/encoder_info_settings.h b/rtc_base/experiments/encoder_info_settings.h index e4dc459fcf..d450697f47 100644 --- a/rtc_base/experiments/encoder_info_settings.h +++ b/rtc_base/experiments/encoder_info_settings.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/video_codecs/video_encoder.h" #include "rtc_base/experiments/field_trial_parser.h" @@ -58,7 +59,7 @@ class EncoderInfoSettings { resolution_bitrate_limits); protected: - explicit EncoderInfoSettings(std::string name); + explicit EncoderInfoSettings(absl::string_view name); private: FieldTrialOptional requested_resolution_alignment_; diff --git a/rtc_base/experiments/field_trial_list.cc b/rtc_base/experiments/field_trial_list.cc index ac3fd88f49..72cd79f2d2 100644 --- a/rtc_base/experiments/field_trial_list.cc +++ b/rtc_base/experiments/field_trial_list.cc @@ -9,9 +9,11 @@ */ #include "rtc_base/experiments/field_trial_list.h" +#include "absl/strings/string_view.h" + namespace webrtc { -FieldTrialListBase::FieldTrialListBase(std::string key) +FieldTrialListBase::FieldTrialListBase(absl::string_view key) : FieldTrialParameterInterface(key), failed_(false), parse_got_called_(false) {} diff --git a/rtc_base/experiments/field_trial_list.h b/rtc_base/experiments/field_trial_list.h index 00425be7b9..7b9f0d9dfc 100644 --- a/rtc_base/experiments/field_trial_list.h +++ b/rtc_base/experiments/field_trial_list.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_parser.h" #include "rtc_base/string_encode.h" @@ -36,7 +37,7 @@ namespace webrtc { class FieldTrialListBase : public FieldTrialParameterInterface { protected: friend class FieldTrialListWrapper; - explicit FieldTrialListBase(std::string key); + explicit FieldTrialListBase(absl::string_view key); bool Failed() const; bool Used() const; @@ -52,8 +53,8 @@ class FieldTrialListBase : public FieldTrialParameterInterface { template class FieldTrialList : public FieldTrialListBase { public: - explicit FieldTrialList(std::string key) : FieldTrialList(key, {}) {} - FieldTrialList(std::string key, std::initializer_list default_values) + explicit FieldTrialList(absl::string_view key) : FieldTrialList(key, {}) {} + FieldTrialList(absl::string_view key, std::initializer_list default_values) : FieldTrialListBase(key), values_(default_values) {} std::vector Get() const { return values_; } @@ -132,7 +133,7 @@ struct LambdaTypeTraits { template struct TypedFieldTrialListWrapper : FieldTrialListWrapper { public: - TypedFieldTrialListWrapper(std::string key, + TypedFieldTrialListWrapper(absl::string_view key, std::function sink) : list_(key), sink_(sink) {} @@ -151,7 +152,8 @@ struct TypedFieldTrialListWrapper : FieldTrialListWrapper { template > -FieldTrialListWrapper* FieldTrialStructMember(std::string key, F accessor) { +FieldTrialListWrapper* FieldTrialStructMember(absl::string_view key, + F accessor) { return new field_trial_list_impl::TypedFieldTrialListWrapper< typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) { *accessor(static_cast(s)) = t; diff --git a/rtc_base/experiments/field_trial_list_unittest.cc b/rtc_base/experiments/field_trial_list_unittest.cc index 6d5e212eea..221a3c6929 100644 --- a/rtc_base/experiments/field_trial_list_unittest.cc +++ b/rtc_base/experiments/field_trial_list_unittest.cc @@ -10,6 +10,7 @@ #include "rtc_base/experiments/field_trial_list.h" +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "test/gmock.h" @@ -25,7 +26,8 @@ struct Garment { // Only needed for testing. Garment() = default; - Garment(int p, std::string c, bool g) : price(p), color(c), has_glitter(g) {} + Garment(int p, absl::string_view c, bool g) + : price(p), color(c), has_glitter(g) {} bool operator==(const Garment& other) const { return price == other.price && color == other.color && diff --git a/rtc_base/experiments/field_trial_parser.cc b/rtc_base/experiments/field_trial_parser.cc index 952250b767..78d5489f5e 100644 --- a/rtc_base/experiments/field_trial_parser.cc +++ b/rtc_base/experiments/field_trial_parser.cc @@ -23,7 +23,8 @@ namespace webrtc { -FieldTrialParameterInterface::FieldTrialParameterInterface(std::string key) +FieldTrialParameterInterface::FieldTrialParameterInterface( + absl::string_view key) : key_(key) {} FieldTrialParameterInterface::~FieldTrialParameterInterface() { RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_ @@ -111,7 +112,7 @@ void ParseFieldTrial( } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { if (str == "true" || str == "1") { return true; } else if (str == "false" || str == "0") { @@ -121,10 +122,10 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { double value; char unit[2]{0, 0}; - if (sscanf(str.c_str(), "%lf%1s", &value, unit) >= 1) { + if (sscanf(std::string(str).c_str(), "%lf%1s", &value, unit) >= 1) { if (unit[0] == '%') return value / 100; return value; @@ -134,9 +135,9 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { int64_t value; - if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } @@ -145,9 +146,9 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { int64_t value; - if (sscanf(str.c_str(), "%" SCNd64, &value) == 1) { + if (sscanf(std::string(str).c_str(), "%" SCNd64, &value) == 1) { if (rtc::IsValueInRangeForNumericType(value)) { return static_cast(value); } @@ -156,34 +157,36 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { - return std::move(str); +absl::optional ParseTypedParameter( + absl::string_view str) { + return std::string(str); } template <> absl::optional> ParseTypedParameter>( - std::string str) { + absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> ParseTypedParameter>( - std::string str) { + absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } -FieldTrialFlag::FieldTrialFlag(std::string key) : FieldTrialFlag(key, false) {} +FieldTrialFlag::FieldTrialFlag(absl::string_view key) + : FieldTrialFlag(key, false) {} -FieldTrialFlag::FieldTrialFlag(std::string key, bool default_value) +FieldTrialFlag::FieldTrialFlag(absl::string_view key, bool default_value) : FieldTrialParameterInterface(key), value_(default_value) {} bool FieldTrialFlag::Get() const { @@ -208,7 +211,7 @@ bool FieldTrialFlag::Parse(absl::optional str_value) { } AbstractFieldTrialEnum::AbstractFieldTrialEnum( - std::string key, + absl::string_view key, int default_value, std::map mapping) : FieldTrialParameterInterface(key), diff --git a/rtc_base/experiments/field_trial_parser.h b/rtc_base/experiments/field_trial_parser.h index bd11eea20a..822895e70b 100644 --- a/rtc_base/experiments/field_trial_parser.h +++ b/rtc_base/experiments/field_trial_parser.h @@ -46,7 +46,7 @@ class FieldTrialParameterInterface { FieldTrialParameterInterface(const FieldTrialParameterInterface&) = default; FieldTrialParameterInterface& operator=(const FieldTrialParameterInterface&) = default; - explicit FieldTrialParameterInterface(std::string key); + explicit FieldTrialParameterInterface(absl::string_view key); friend void ParseFieldTrial( std::initializer_list fields, absl::string_view trial_string); @@ -71,14 +71,14 @@ void ParseFieldTrial( // Specialize this in code file for custom types. Should return absl::nullopt if // the given string cannot be properly parsed. template -absl::optional ParseTypedParameter(std::string); +absl::optional ParseTypedParameter(absl::string_view); // This class uses the ParseTypedParameter function to implement a parameter // implementation with an enforced default value. template class FieldTrialParameter : public FieldTrialParameterInterface { public: - FieldTrialParameter(std::string key, T default_value) + FieldTrialParameter(absl::string_view key, T default_value) : FieldTrialParameterInterface(key), value_(default_value) {} T Get() const { return value_; } operator T() const { return Get(); } @@ -108,7 +108,7 @@ class FieldTrialParameter : public FieldTrialParameterInterface { template class FieldTrialConstrained : public FieldTrialParameterInterface { public: - FieldTrialConstrained(std::string key, + FieldTrialConstrained(absl::string_view key, T default_value, absl::optional lower_limit, absl::optional upper_limit) @@ -141,7 +141,7 @@ class FieldTrialConstrained : public FieldTrialParameterInterface { class AbstractFieldTrialEnum : public FieldTrialParameterInterface { public: - AbstractFieldTrialEnum(std::string key, + AbstractFieldTrialEnum(absl::string_view key, int default_value, std::map mapping); ~AbstractFieldTrialEnum() override; @@ -162,7 +162,7 @@ class AbstractFieldTrialEnum : public FieldTrialParameterInterface { template class FieldTrialEnum : public AbstractFieldTrialEnum { public: - FieldTrialEnum(std::string key, + FieldTrialEnum(absl::string_view key, T default_value, std::map mapping) : AbstractFieldTrialEnum(key, @@ -185,9 +185,9 @@ class FieldTrialEnum : public AbstractFieldTrialEnum { template class FieldTrialOptional : public FieldTrialParameterInterface { public: - explicit FieldTrialOptional(std::string key) + explicit FieldTrialOptional(absl::string_view key) : FieldTrialParameterInterface(key) {} - FieldTrialOptional(std::string key, absl::optional default_value) + FieldTrialOptional(absl::string_view key, absl::optional default_value) : FieldTrialParameterInterface(key), value_(default_value) {} absl::optional GetOptional() const { return value_; } const T& Value() const { return value_.value(); } @@ -217,8 +217,8 @@ class FieldTrialOptional : public FieldTrialParameterInterface { // explicit value is provided, the flag evaluates to true. class FieldTrialFlag : public FieldTrialParameterInterface { public: - explicit FieldTrialFlag(std::string key); - FieldTrialFlag(std::string key, bool default_value); + explicit FieldTrialFlag(absl::string_view key); + FieldTrialFlag(absl::string_view key, bool default_value); bool Get() const; explicit operator bool() const; @@ -230,7 +230,8 @@ class FieldTrialFlag : public FieldTrialParameterInterface { }; template -absl::optional> ParseOptionalParameter(std::string str) { +absl::optional> ParseOptionalParameter( + absl::string_view str) { if (str.empty()) return absl::optional(); auto parsed = ParseTypedParameter(str); @@ -240,28 +241,29 @@ absl::optional> ParseOptionalParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter( + absl::string_view str); template <> absl::optional> ParseTypedParameter>( - std::string str); + absl::string_view str); template <> absl::optional> ParseTypedParameter>( - std::string str); + absl::string_view str); template <> absl::optional> -ParseTypedParameter>(std::string str); +ParseTypedParameter>(absl::string_view str); template <> absl::optional> -ParseTypedParameter>(std::string str); +ParseTypedParameter>(absl::string_view str); // Accepts true, false, else parsed with sscanf %i, true if != 0. extern template class FieldTrialParameter; diff --git a/rtc_base/experiments/field_trial_parser_unittest.cc b/rtc_base/experiments/field_trial_parser_unittest.cc index d36b3c7d95..9916edee97 100644 --- a/rtc_base/experiments/field_trial_parser_unittest.cc +++ b/rtc_base/experiments/field_trial_parser_unittest.cc @@ -9,6 +9,7 @@ */ #include "rtc_base/experiments/field_trial_parser.h" +#include "absl/strings/string_view.h" #include "rtc_base/experiments/field_trial_list.h" #include "rtc_base/gunit.h" #include "system_wrappers/include/field_trial.h" @@ -28,7 +29,7 @@ struct DummyExperiment { FieldTrialParameter hash = FieldTrialParameter("h", "a80"); - explicit DummyExperiment(std::string field_trial) { + explicit DummyExperiment(absl::string_view field_trial) { ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash}, field_trial); } diff --git a/rtc_base/experiments/field_trial_units.cc b/rtc_base/experiments/field_trial_units.cc index 5aceab76a0..92af46a9e3 100644 --- a/rtc_base/experiments/field_trial_units.cc +++ b/rtc_base/experiments/field_trial_units.cc @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" // Large enough to fit "seconds", the longest supported unit name. @@ -28,7 +29,7 @@ struct ValueWithUnit { std::string unit; }; -absl::optional ParseValueWithUnit(std::string str) { +absl::optional ParseValueWithUnit(absl::string_view str) { if (str == "inf") { return ValueWithUnit{std::numeric_limits::infinity(), ""}; } else if (str == "-inf") { @@ -37,8 +38,8 @@ absl::optional ParseValueWithUnit(std::string str) { double double_val; char unit_char[RTC_TRIAL_UNIT_SIZE]; unit_char[0] = 0; - if (sscanf(str.c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s", &double_val, - unit_char) >= 1) { + if (sscanf(std::string(str).c_str(), "%lf%" RTC_TRIAL_UNIT_LENGTH_STR "s", + &double_val, unit_char) >= 1) { return ValueWithUnit{double_val, unit_char}; } } @@ -47,7 +48,7 @@ absl::optional ParseValueWithUnit(std::string str) { } // namespace template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit.empty() || result->unit == "kbps") { @@ -60,7 +61,7 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter(absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit.empty() || result->unit == "bytes") @@ -70,7 +71,8 @@ absl::optional ParseTypedParameter(std::string str) { } template <> -absl::optional ParseTypedParameter(std::string str) { +absl::optional ParseTypedParameter( + absl::string_view str) { absl::optional result = ParseValueWithUnit(str); if (result) { if (result->unit == "s" || result->unit == "seconds") { @@ -86,17 +88,17 @@ absl::optional ParseTypedParameter(std::string str) { template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } template <> absl::optional> -ParseTypedParameter>(std::string str) { +ParseTypedParameter>(absl::string_view str) { return ParseOptionalParameter(str); } diff --git a/rtc_base/experiments/field_trial_units.h b/rtc_base/experiments/field_trial_units.h index d85b2f04ba..408367c031 100644 --- a/rtc_base/experiments/field_trial_units.h +++ b/rtc_base/experiments/field_trial_units.h @@ -10,6 +10,7 @@ #ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ #define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_UNITS_H_ +#include "absl/strings/string_view.h" #include "api/units/data_rate.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" @@ -18,11 +19,11 @@ namespace webrtc { template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); template <> -absl::optional ParseTypedParameter(std::string str); +absl::optional ParseTypedParameter(absl::string_view str); extern template class FieldTrialParameter; extern template class FieldTrialParameter; diff --git a/rtc_base/experiments/field_trial_units_unittest.cc b/rtc_base/experiments/field_trial_units_unittest.cc index 1f46d6f9ee..8996663d8e 100644 --- a/rtc_base/experiments/field_trial_units_unittest.cc +++ b/rtc_base/experiments/field_trial_units_unittest.cc @@ -11,6 +11,7 @@ #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "rtc_base/experiments/field_trial_parser.h" #include "test/gtest.h" @@ -25,7 +26,7 @@ struct DummyExperiment { FieldTrialOptional max_buffer = FieldTrialOptional("b", absl::nullopt); - explicit DummyExperiment(std::string field_trial) { + explicit DummyExperiment(absl::string_view field_trial) { ParseFieldTrial({&target_rate, &max_buffer, &period}, field_trial); } }; diff --git a/rtc_base/experiments/keyframe_interval_settings.cc b/rtc_base/experiments/keyframe_interval_settings.cc index 76c85cbbad..413e2a91d5 100644 --- a/rtc_base/experiments/keyframe_interval_settings.cc +++ b/rtc_base/experiments/keyframe_interval_settings.cc @@ -21,7 +21,7 @@ constexpr char kFieldTrialName[] = "WebRTC-KeyframeInterval"; } // namespace KeyframeIntervalSettings::KeyframeIntervalSettings( - const WebRtcKeyValueConfig* const key_value_config) + const FieldTrialsView* const key_value_config) : min_keyframe_send_interval_ms_("min_keyframe_send_interval_ms") { ParseFieldTrial({&min_keyframe_send_interval_ms_}, key_value_config->Lookup(kFieldTrialName)); diff --git a/rtc_base/experiments/keyframe_interval_settings.h b/rtc_base/experiments/keyframe_interval_settings.h index 3f253f0022..aff7854516 100644 --- a/rtc_base/experiments/keyframe_interval_settings.h +++ b/rtc_base/experiments/keyframe_interval_settings.h @@ -12,7 +12,7 @@ #define RTC_BASE_EXPERIMENTS_KEYFRAME_INTERVAL_SETTINGS_H_ #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -29,8 +29,7 @@ class KeyframeIntervalSettings final { absl::optional MinKeyframeSendIntervalMs() const; private: - explicit KeyframeIntervalSettings( - const WebRtcKeyValueConfig* key_value_config); + explicit KeyframeIntervalSettings(const FieldTrialsView* key_value_config); FieldTrialOptional min_keyframe_send_interval_ms_; }; diff --git a/rtc_base/experiments/quality_rampup_experiment.cc b/rtc_base/experiments/quality_rampup_experiment.cc index 35c83f7011..509ba91dc3 100644 --- a/rtc_base/experiments/quality_rampup_experiment.cc +++ b/rtc_base/experiments/quality_rampup_experiment.cc @@ -18,7 +18,7 @@ namespace webrtc { QualityRampupExperiment::QualityRampupExperiment( - const WebRtcKeyValueConfig* const key_value_config) + const FieldTrialsView* const key_value_config) : min_pixels_("min_pixels"), min_duration_ms_("min_duration_ms"), max_bitrate_factor_("max_bitrate_factor") { diff --git a/rtc_base/experiments/quality_rampup_experiment.h b/rtc_base/experiments/quality_rampup_experiment.h index 719b1893f6..e8048a3c1c 100644 --- a/rtc_base/experiments/quality_rampup_experiment.h +++ b/rtc_base/experiments/quality_rampup_experiment.h @@ -12,7 +12,7 @@ #define RTC_BASE_EXPERIMENTS_QUALITY_RAMPUP_EXPERIMENT_H_ #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -38,7 +38,7 @@ class QualityRampupExperiment final { private: explicit QualityRampupExperiment( - const WebRtcKeyValueConfig* const key_value_config); + const FieldTrialsView* const key_value_config); FieldTrialOptional min_pixels_; FieldTrialOptional min_duration_ms_; diff --git a/rtc_base/experiments/quality_scaler_settings.cc b/rtc_base/experiments/quality_scaler_settings.cc index d2443b05ce..85c99255ab 100644 --- a/rtc_base/experiments/quality_scaler_settings.cc +++ b/rtc_base/experiments/quality_scaler_settings.cc @@ -20,7 +20,7 @@ const double kMinScaleFactor = 0.01; } // namespace QualityScalerSettings::QualityScalerSettings( - const WebRtcKeyValueConfig* const key_value_config) + const FieldTrialsView* const key_value_config) : sampling_period_ms_("sampling_period_ms"), average_qp_window_("average_qp_window"), min_frames_("min_frames"), diff --git a/rtc_base/experiments/quality_scaler_settings.h b/rtc_base/experiments/quality_scaler_settings.h index b4b6a427a0..99827aac6b 100644 --- a/rtc_base/experiments/quality_scaler_settings.h +++ b/rtc_base/experiments/quality_scaler_settings.h @@ -12,7 +12,7 @@ #define RTC_BASE_EXPERIMENTS_QUALITY_SCALER_SETTINGS_H_ #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -30,8 +30,7 @@ class QualityScalerSettings final { absl::optional InitialBitrateFactor() const; private: - explicit QualityScalerSettings( - const WebRtcKeyValueConfig* const key_value_config); + explicit QualityScalerSettings(const FieldTrialsView* const key_value_config); FieldTrialOptional sampling_period_ms_; FieldTrialOptional average_qp_window_; diff --git a/rtc_base/experiments/rate_control_settings.cc b/rtc_base/experiments/rate_control_settings.cc index bed194e683..91b475a04a 100644 --- a/rtc_base/experiments/rate_control_settings.cc +++ b/rtc_base/experiments/rate_control_settings.cc @@ -39,12 +39,12 @@ const char* kVideoHysteresisFieldTrialname = const char* kScreenshareHysteresisFieldTrialname = "WebRTC-SimulcastScreenshareUpswitchHysteresisPercent"; -bool IsEnabled(const WebRtcKeyValueConfig* const key_value_config, +bool IsEnabled(const FieldTrialsView* const key_value_config, absl::string_view key) { return absl::StartsWith(key_value_config->Lookup(key), "Enabled"); } -void ParseHysteresisFactor(const WebRtcKeyValueConfig* const key_value_config, +void ParseHysteresisFactor(const FieldTrialsView* const key_value_config, absl::string_view key, double* output_value) { std::string group_name = key_value_config->Lookup(key); @@ -94,7 +94,7 @@ std::unique_ptr VideoRateControlConfig::Parser() { } RateControlSettings::RateControlSettings( - const WebRtcKeyValueConfig* const key_value_config) { + const FieldTrialsView* const key_value_config) { std::string congestion_window_config = key_value_config->Lookup(CongestionWindowConfig::kKey).empty() ? kCongestionWindowDefaultFieldTrialString @@ -120,7 +120,7 @@ RateControlSettings RateControlSettings::ParseFromFieldTrials() { } RateControlSettings RateControlSettings::ParseFromKeyValueConfig( - const WebRtcKeyValueConfig* const key_value_config) { + const FieldTrialsView* const key_value_config) { FieldTrialBasedConfig field_trial_config; return RateControlSettings(key_value_config ? key_value_config : &field_trial_config); diff --git a/rtc_base/experiments/rate_control_settings.h b/rtc_base/experiments/rate_control_settings.h index 1c38e927dc..bad16d2eee 100644 --- a/rtc_base/experiments/rate_control_settings.h +++ b/rtc_base/experiments/rate_control_settings.h @@ -12,7 +12,7 @@ #define RTC_BASE_EXPERIMENTS_RATE_CONTROL_SETTINGS_H_ #include "absl/types/optional.h" -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "api/units/data_size.h" #include "api/video_codecs/video_codec.h" #include "api/video_codecs/video_encoder_config.h" @@ -57,7 +57,7 @@ class RateControlSettings final { static RateControlSettings ParseFromFieldTrials(); static RateControlSettings ParseFromKeyValueConfig( - const WebRtcKeyValueConfig* const key_value_config); + const FieldTrialsView* const key_value_config); // When CongestionWindowPushback is enabled, the pacer is oblivious to // the congestion window. The relation between outstanding data and @@ -93,8 +93,7 @@ class RateControlSettings final { bool BitrateAdjusterCanUseNetworkHeadroom() const; private: - explicit RateControlSettings( - const WebRtcKeyValueConfig* const key_value_config); + explicit RateControlSettings(const FieldTrialsView* const key_value_config); CongestionWindowConfig congestion_window_config_; VideoRateControlConfig video_config_; diff --git a/rtc_base/experiments/stable_target_rate_experiment.cc b/rtc_base/experiments/stable_target_rate_experiment.cc index fa7a97b51f..b7246d4a12 100644 --- a/rtc_base/experiments/stable_target_rate_experiment.cc +++ b/rtc_base/experiments/stable_target_rate_experiment.cc @@ -19,7 +19,7 @@ constexpr char kFieldTrialName[] = "WebRTC-StableTargetRate"; } // namespace StableTargetRateExperiment::StableTargetRateExperiment( - const WebRtcKeyValueConfig* const key_value_config, + const FieldTrialsView* const key_value_config, double default_video_hysteresis, double default_screenshare_hysteresis) : enabled_("enabled", false), @@ -43,7 +43,7 @@ StableTargetRateExperiment StableTargetRateExperiment::ParseFromFieldTrials() { } StableTargetRateExperiment StableTargetRateExperiment::ParseFromKeyValueConfig( - const WebRtcKeyValueConfig* const key_value_config) { + const FieldTrialsView* const key_value_config) { RateControlSettings rate_control = RateControlSettings::ParseFromKeyValueConfig(key_value_config); return StableTargetRateExperiment( diff --git a/rtc_base/experiments/stable_target_rate_experiment.h b/rtc_base/experiments/stable_target_rate_experiment.h index 299299ce87..be0f9da129 100644 --- a/rtc_base/experiments/stable_target_rate_experiment.h +++ b/rtc_base/experiments/stable_target_rate_experiment.h @@ -11,7 +11,7 @@ #ifndef RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_ #define RTC_BASE_EXPERIMENTS_STABLE_TARGET_RATE_EXPERIMENT_H_ -#include "api/transport/webrtc_key_value_config.h" +#include "api/field_trials_view.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -22,7 +22,7 @@ class StableTargetRateExperiment { StableTargetRateExperiment(StableTargetRateExperiment&&); static StableTargetRateExperiment ParseFromFieldTrials(); static StableTargetRateExperiment ParseFromKeyValueConfig( - const WebRtcKeyValueConfig* const key_value_config); + const FieldTrialsView* const key_value_config); bool IsEnabled() const; double GetVideoHysteresisFactor() const; @@ -30,7 +30,7 @@ class StableTargetRateExperiment { private: explicit StableTargetRateExperiment( - const WebRtcKeyValueConfig* const key_value_config, + const FieldTrialsView* const key_value_config, double default_video_hysteresis, double default_screenshare_hysteresis); diff --git a/rtc_base/experiments/struct_parameters_parser.cc b/rtc_base/experiments/struct_parameters_parser.cc index d62eb6f1ea..011df3eaba 100644 --- a/rtc_base/experiments/struct_parameters_parser.cc +++ b/rtc_base/experiments/struct_parameters_parser.cc @@ -11,13 +11,14 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/logging.h" namespace webrtc { namespace { size_t FindOrEnd(absl::string_view str, size_t start, char delimiter) { size_t pos = str.find(delimiter, start); - pos = (pos == std::string::npos) ? str.length() : pos; + pos = (pos == absl::string_view::npos) ? str.length() : pos; return pos; } } // namespace diff --git a/rtc_base/fake_mdns_responder.h b/rtc_base/fake_mdns_responder.h index 1f87cf4b81..457f959f29 100644 --- a/rtc_base/fake_mdns_responder.h +++ b/rtc_base/fake_mdns_responder.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ip_address.h" #include "rtc_base/location.h" #include "rtc_base/mdns_responder_interface.h" @@ -53,7 +54,7 @@ class FakeMdnsResponder : public MdnsResponderInterface { thread_->PostTask(ToQueuedTask([callback, result]() { callback(result); })); } - rtc::IPAddress GetMappedAddressForName(const std::string& name) const { + rtc::IPAddress GetMappedAddressForName(absl::string_view name) const { for (const auto& addr_name_pair : addr_name_map_) { if (addr_name_pair.second == name) { return addr_name_pair.first; diff --git a/rtc_base/fake_network.h b/rtc_base/fake_network.h index 53664cb8f8..cec2ffc75c 100644 --- a/rtc_base/fake_network.h +++ b/rtc_base/fake_network.h @@ -48,13 +48,13 @@ class FakeNetworkManager : public NetworkManagerBase, AddInterface(iface, "test" + rtc::ToString(next_index_++)); } - void AddInterface(const SocketAddress& iface, const std::string& if_name) { + void AddInterface(const SocketAddress& iface, absl::string_view if_name) { AddInterface(iface, if_name, ADAPTER_TYPE_UNKNOWN); } void AddInterface( const SocketAddress& iface, - const std::string& if_name, + absl::string_view if_name, AdapterType type, absl::optional underlying_vpn_adapter_type = absl::nullopt) { SocketAddress address(if_name, 0); @@ -116,7 +116,7 @@ class FakeNetworkManager : public NetworkManagerBase, void DoUpdateNetworks() { if (start_count_ == 0) return; - std::vector networks; + std::vector> networks; for (IfaceList::iterator it = ifaces_.begin(); it != ifaces_.end(); ++it) { int prefix_length = 0; if (it->socket_address.ipaddr().family() == AF_INET) { @@ -125,18 +125,18 @@ class FakeNetworkManager : public NetworkManagerBase, prefix_length = kFakeIPv6NetworkPrefixLength; } IPAddress prefix = TruncateIP(it->socket_address.ipaddr(), prefix_length); - std::unique_ptr net(new Network( + auto net = std::make_unique( it->socket_address.hostname(), it->socket_address.hostname(), prefix, - prefix_length, it->adapter_type)); + prefix_length, it->adapter_type); if (it->underlying_vpn_adapter_type.has_value()) { net->set_underlying_type_for_vpn(*it->underlying_vpn_adapter_type); } net->set_default_local_address_provider(this); net->AddIP(it->socket_address.ipaddr()); - networks.push_back(net.release()); + networks.push_back(std::move(net)); } bool changed; - MergeNetworkList(networks, &changed); + MergeNetworkList(std::move(networks), &changed); if (changed || !sent_first_update_) { SignalNetworksChanged(); sent_first_update_ = true; diff --git a/rtc_base/fake_ssl_identity.cc b/rtc_base/fake_ssl_identity.cc index 87ede73985..73c843a2e7 100644 --- a/rtc_base/fake_ssl_identity.cc +++ b/rtc_base/fake_ssl_identity.cc @@ -14,12 +14,13 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/message_digest.h" namespace rtc { -FakeSSLCertificate::FakeSSLCertificate(const std::string& pem_string) +FakeSSLCertificate::FakeSSLCertificate(absl::string_view pem_string) : pem_string_(pem_string), digest_algorithm_(DIGEST_SHA_1), expiration_time_(-1) {} @@ -51,8 +52,8 @@ void FakeSSLCertificate::SetCertificateExpirationTime(int64_t expiration_time) { expiration_time_ = expiration_time; } -void FakeSSLCertificate::set_digest_algorithm(const std::string& algorithm) { - digest_algorithm_ = algorithm; +void FakeSSLCertificate::set_digest_algorithm(absl::string_view algorithm) { + digest_algorithm_ = std::string(algorithm); } bool FakeSSLCertificate::GetSignatureDigestAlgorithm( @@ -61,7 +62,7 @@ bool FakeSSLCertificate::GetSignatureDigestAlgorithm( return true; } -bool FakeSSLCertificate::ComputeDigest(const std::string& algorithm, +bool FakeSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { @@ -70,7 +71,7 @@ bool FakeSSLCertificate::ComputeDigest(const std::string& algorithm, return (*length != 0); } -FakeSSLIdentity::FakeSSLIdentity(const std::string& pem_string) +FakeSSLIdentity::FakeSSLIdentity(absl::string_view pem_string) : FakeSSLIdentity(FakeSSLCertificate(pem_string)) {} FakeSSLIdentity::FakeSSLIdentity(const std::vector& pem_strings) { diff --git a/rtc_base/fake_ssl_identity.h b/rtc_base/fake_ssl_identity.h index 512baba9fb..2b4ae2e57a 100644 --- a/rtc_base/fake_ssl_identity.h +++ b/rtc_base/fake_ssl_identity.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -23,7 +24,7 @@ class FakeSSLCertificate : public SSLCertificate { public: // SHA-1 is the default digest algorithm because it is available in all build // configurations used for unit testing. - explicit FakeSSLCertificate(const std::string& pem_string); + explicit FakeSSLCertificate(absl::string_view pem_string); FakeSSLCertificate(const FakeSSLCertificate&); ~FakeSSLCertificate() override; @@ -34,14 +35,14 @@ class FakeSSLCertificate : public SSLCertificate { void ToDER(Buffer* der_buffer) const override; int64_t CertificateExpirationTime() const override; bool GetSignatureDigestAlgorithm(std::string* algorithm) const override; - bool ComputeDigest(const std::string& algorithm, + bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const override; void SetCertificateExpirationTime(int64_t expiration_time); - void set_digest_algorithm(const std::string& algorithm); + void set_digest_algorithm(absl::string_view algorithm); private: std::string pem_string_; @@ -52,7 +53,7 @@ class FakeSSLCertificate : public SSLCertificate { class FakeSSLIdentity : public SSLIdentity { public: - explicit FakeSSLIdentity(const std::string& pem_string); + explicit FakeSSLIdentity(absl::string_view pem_string); // For a certificate chain. explicit FakeSSLIdentity(const std::vector& pem_strings); explicit FakeSSLIdentity(const FakeSSLCertificate& cert); diff --git a/rtc_base/file_rotating_stream.cc b/rtc_base/file_rotating_stream.cc index 5a004a937b..c529b5b1b4 100644 --- a/rtc_base/file_rotating_stream.cc +++ b/rtc_base/file_rotating_stream.cc @@ -14,6 +14,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include @@ -29,6 +31,7 @@ #include "absl/types/optional.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/strings/string_builder.h" // Note: We use fprintf for logging in the write paths of this stream to avoid // infinite loops when logging. @@ -39,54 +42,58 @@ namespace { const char kCallSessionLogPrefix[] = "webrtc_log"; -std::string AddTrailingPathDelimiterIfNeeded(std::string directory); +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory); // `dir` must have a trailing delimiter. `prefix` must not include wild card // characters. -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix); -bool DeleteFile(const std::string& file); -bool MoveFile(const std::string& old_file, const std::string& new_file); -bool IsFile(const std::string& file); -bool IsFolder(const std::string& file); -absl::optional GetFileSize(const std::string& file); +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix); +bool DeleteFile(absl::string_view file); +bool MoveFile(absl::string_view old_file, absl::string_view new_file); +bool IsFile(absl::string_view file); +bool IsFolder(absl::string_view file); +absl::optional GetFileSize(absl::string_view file); #if defined(WEBRTC_WIN) -std::string AddTrailingPathDelimiterIfNeeded(std::string directory) { +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) { if (absl::EndsWith(directory, "\\")) { - return directory; + return std::string(directory); } - return directory + "\\"; + return std::string(directory) + "\\"; } -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix) { +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix) { RTC_DCHECK(absl::EndsWith(directory, "\\")); WIN32_FIND_DATAW data; HANDLE handle; - handle = ::FindFirstFileW(ToUtf16(directory + prefix + '*').c_str(), &data); + StringBuilder pattern_builder{directory}; + pattern_builder << prefix << "*"; + handle = ::FindFirstFileW(ToUtf16(pattern_builder.str()).c_str(), &data); if (handle == INVALID_HANDLE_VALUE) return {}; std::vector file_list; do { - file_list.emplace_back(directory + ToUtf8(data.cFileName)); + StringBuilder file_builder{directory}; + file_builder << ToUtf8(data.cFileName); + file_list.emplace_back(file_builder.Release()); } while (::FindNextFileW(handle, &data) == TRUE); ::FindClose(handle); return file_list; } -bool DeleteFile(const std::string& file) { +bool DeleteFile(absl::string_view file) { return ::DeleteFileW(ToUtf16(file).c_str()) != 0; } -bool MoveFile(const std::string& old_file, const std::string& new_file) { +bool MoveFile(absl::string_view old_file, absl::string_view new_file) { return ::MoveFileW(ToUtf16(old_file).c_str(), ToUtf16(new_file).c_str()) != 0; } -bool IsFile(const std::string& file) { +bool IsFile(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data)) @@ -94,7 +101,7 @@ bool IsFile(const std::string& file) { return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; } -bool IsFolder(const std::string& file) { +bool IsFolder(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (0 == ::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data)) @@ -103,7 +110,7 @@ bool IsFolder(const std::string& file) { FILE_ATTRIBUTE_DIRECTORY; } -absl::optional GetFileSize(const std::string& file) { +absl::optional GetFileSize(absl::string_view file) { WIN32_FILE_ATTRIBUTE_DATA data = {0}; if (::GetFileAttributesExW(ToUtf16(file).c_str(), GetFileExInfoStandard, &data) == 0) @@ -113,55 +120,57 @@ absl::optional GetFileSize(const std::string& file) { #else // defined(WEBRTC_WIN) -std::string AddTrailingPathDelimiterIfNeeded(std::string directory) { +std::string AddTrailingPathDelimiterIfNeeded(absl::string_view directory) { if (absl::EndsWith(directory, "/")) { - return directory; + return std::string(directory); } - return directory + "/"; + return std::string(directory) + "/"; } -std::vector GetFilesWithPrefix(const std::string& directory, - const std::string& prefix) { +std::vector GetFilesWithPrefix(absl::string_view directory, + absl::string_view prefix) { RTC_DCHECK(absl::EndsWith(directory, "/")); - DIR* dir = ::opendir(directory.c_str()); + std::string directory_str = std::string(directory); + DIR* dir = ::opendir(directory_str.c_str()); if (dir == nullptr) return {}; std::vector file_list; for (struct dirent* dirent = ::readdir(dir); dirent; dirent = ::readdir(dir)) { std::string name = dirent->d_name; - if (name.compare(0, prefix.size(), prefix) == 0) { - file_list.emplace_back(directory + name); + if (name.compare(0, prefix.size(), prefix.data(), prefix.size()) == 0) { + file_list.emplace_back(directory_str + name); } } ::closedir(dir); return file_list; } -bool DeleteFile(const std::string& file) { - return ::unlink(file.c_str()) == 0; +bool DeleteFile(absl::string_view file) { + return ::unlink(std::string(file).c_str()) == 0; } -bool MoveFile(const std::string& old_file, const std::string& new_file) { - return ::rename(old_file.c_str(), new_file.c_str()) == 0; +bool MoveFile(absl::string_view old_file, absl::string_view new_file) { + return ::rename(std::string(old_file).c_str(), + std::string(new_file).c_str()) == 0; } -bool IsFile(const std::string& file) { +bool IsFile(absl::string_view file) { struct stat st; - int res = ::stat(file.c_str(), &st); + int res = ::stat(std::string(file).c_str(), &st); // Treat symlinks, named pipes, etc. all as files. return res == 0 && !S_ISDIR(st.st_mode); } -bool IsFolder(const std::string& file) { +bool IsFolder(absl::string_view file) { struct stat st; - int res = ::stat(file.c_str(), &st); + int res = ::stat(std::string(file).c_str(), &st); return res == 0 && S_ISDIR(st.st_mode); } -absl::optional GetFileSize(const std::string& file) { +absl::optional GetFileSize(absl::string_view file) { struct stat st; - if (::stat(file.c_str(), &st) != 0) + if (::stat(std::string(file).c_str(), &st) != 0) return absl::nullopt; return st.st_size; } @@ -170,8 +179,8 @@ absl::optional GetFileSize(const std::string& file) { } // namespace -FileRotatingStream::FileRotatingStream(const std::string& dir_path, - const std::string& file_prefix, +FileRotatingStream::FileRotatingStream(absl::string_view dir_path, + absl::string_view file_prefix, size_t max_file_size, size_t num_files) : dir_path_(AddTrailingPathDelimiterIfNeeded(dir_path)), @@ -331,7 +340,7 @@ std::string FileRotatingStream::GetFilePath(size_t index, } CallSessionFileRotatingStream::CallSessionFileRotatingStream( - const std::string& dir_path, + absl::string_view dir_path, size_t max_total_log_size) : FileRotatingStream(dir_path, kCallSessionLogPrefix, @@ -376,8 +385,8 @@ size_t CallSessionFileRotatingStream::GetNumRotatingLogFiles( } FileRotatingStreamReader::FileRotatingStreamReader( - const std::string& dir_path, - const std::string& file_prefix) { + absl::string_view dir_path, + absl::string_view file_prefix) { file_names_ = GetFilesWithPrefix(AddTrailingPathDelimiterIfNeeded(dir_path), file_prefix); @@ -413,7 +422,7 @@ size_t FileRotatingStreamReader::ReadAll(void* buffer, size_t size) const { } CallSessionFileRotatingStreamReader::CallSessionFileRotatingStreamReader( - const std::string& dir_path) + absl::string_view dir_path) : FileRotatingStreamReader(dir_path, kCallSessionLogPrefix) {} } // namespace rtc diff --git a/rtc_base/file_rotating_stream.h b/rtc_base/file_rotating_stream.h index 6aaa0bd783..6ae2753098 100644 --- a/rtc_base/file_rotating_stream.h +++ b/rtc_base/file_rotating_stream.h @@ -17,6 +17,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/system/file_wrapper.h" namespace rtc { @@ -29,8 +30,8 @@ class FileRotatingStream { public: // Use this constructor for writing to a directory. Files in the directory // matching the prefix will be deleted on open. - FileRotatingStream(const std::string& dir_path, - const std::string& file_prefix, + FileRotatingStream(absl::string_view dir_path, + absl::string_view file_prefix, size_t max_file_size, size_t num_files); @@ -126,7 +127,7 @@ class CallSessionFileRotatingStream : public FileRotatingStream { // Use this constructor for writing to a directory. Files in the directory // matching what's used by the stream will be deleted. `max_total_log_size` // must be at least 4. - CallSessionFileRotatingStream(const std::string& dir_path, + CallSessionFileRotatingStream(absl::string_view dir_path, size_t max_total_log_size); ~CallSessionFileRotatingStream() override {} @@ -152,8 +153,8 @@ class CallSessionFileRotatingStream : public FileRotatingStream { // directory at construction time. class FileRotatingStreamReader { public: - FileRotatingStreamReader(const std::string& dir_path, - const std::string& file_prefix); + FileRotatingStreamReader(absl::string_view dir_path, + absl::string_view file_prefix); ~FileRotatingStreamReader(); size_t GetSize() const; size_t ReadAll(void* buffer, size_t size) const; @@ -164,7 +165,7 @@ class FileRotatingStreamReader { class CallSessionFileRotatingStreamReader : public FileRotatingStreamReader { public: - CallSessionFileRotatingStreamReader(const std::string& dir_path); + CallSessionFileRotatingStreamReader(absl::string_view dir_path); }; } // namespace rtc diff --git a/rtc_base/file_rotating_stream_unittest.cc b/rtc_base/file_rotating_stream_unittest.cc index 849b111148..1d1e5b62cb 100644 --- a/rtc_base/file_rotating_stream_unittest.cc +++ b/rtc_base/file_rotating_stream_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "test/gtest.h" #include "test/testsupport/file_utils.h" @@ -44,17 +45,17 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { static const char* kFilePrefix; static const size_t kMaxFileSize; - void Init(const std::string& dir_name, - const std::string& file_prefix, + void Init(absl::string_view dir_name, + absl::string_view file_prefix, size_t max_file_size, size_t num_log_files, bool ensure_trailing_delimiter = true) { dir_path_ = webrtc::test::OutputPath(); // Append per-test output path in order to run within gtest parallel. - dir_path_.append(dir_name); + dir_path_.append(dir_name.begin(), dir_name.end()); if (ensure_trailing_delimiter) { - dir_path_.append(webrtc::test::kPathDelimiter); + dir_path_.append(std::string(webrtc::test::kPathDelimiter)); } ASSERT_TRUE(webrtc::test::CreateDir(dir_path_)); stream_.reset(new FileRotatingStream(dir_path_, file_prefix, max_file_size, @@ -78,27 +79,28 @@ class MAYBE_FileRotatingStreamTest : public ::testing::Test { // Checks that the stream reads in the expected contents and then returns an // end of stream result. - void VerifyStreamRead(const char* expected_contents, - const size_t expected_length, - const std::string& dir_path, - const char* file_prefix) { + void VerifyStreamRead(absl::string_view expected_contents, + absl::string_view dir_path, + absl::string_view file_prefix) { + size_t expected_length = expected_contents.size(); FileRotatingStreamReader reader(dir_path, file_prefix); EXPECT_EQ(reader.GetSize(), expected_length); std::unique_ptr buffer(new uint8_t[expected_length]); memset(buffer.get(), 0, expected_length); EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length)); - EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length)); + EXPECT_EQ(0, + memcmp(expected_contents.data(), buffer.get(), expected_length)); } - void VerifyFileContents(const char* expected_contents, - const size_t expected_length, - const std::string& file_path) { + void VerifyFileContents(absl::string_view expected_contents, + absl::string_view file_path) { + size_t expected_length = expected_contents.size(); std::unique_ptr buffer(new uint8_t[expected_length + 1]); webrtc::FileWrapper f = webrtc::FileWrapper::OpenReadOnly(file_path); ASSERT_TRUE(f.is_open()); size_t size_read = f.Read(buffer.get(), expected_length + 1); EXPECT_EQ(size_read, expected_length); - EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), + EXPECT_EQ(0, memcmp(expected_contents.data(), buffer.get(), std::min(expected_length, size_read))); } @@ -149,8 +151,7 @@ TEST_F(MAYBE_FileRotatingStreamTest, WriteAndRead) { WriteAndFlush(message.c_str(), message.size()); // Since the max log size is 2, we will be causing rotation. Read from the // next file. - VerifyFileContents(message.c_str(), message.size(), - stream_->GetFilePath(1)); + VerifyFileContents(message, stream_->GetFilePath(1)); } // Check that exactly three files exist. for (size_t i = 0; i < arraysize(messages); ++i) { @@ -165,8 +166,7 @@ TEST_F(MAYBE_FileRotatingStreamTest, WriteAndRead) { // Reopen for read. std::string expected_contents("bbccd"); - VerifyStreamRead(expected_contents.c_str(), expected_contents.size(), - dir_path_, kFilePrefix); + VerifyStreamRead(expected_contents, dir_path_, kFilePrefix); } // Tests that a write operation (with dir name without delimiter) followed by a @@ -189,8 +189,9 @@ TEST_F(MAYBE_FileRotatingStreamTest, WriteWithoutDelimiterAndRead) { // Reopen for read. std::string expected_contents("bbccd"); - VerifyStreamRead(expected_contents.c_str(), expected_contents.size(), - dir_path_ + webrtc::test::kPathDelimiter, kFilePrefix); + VerifyStreamRead(expected_contents, + dir_path_ + std::string(webrtc::test::kPathDelimiter), + kFilePrefix); } // Tests that a write operation followed by a read (without trailing delimiter) @@ -212,8 +213,8 @@ TEST_F(MAYBE_FileRotatingStreamTest, WriteAndReadWithoutDelimiter) { // Reopen for read. std::string expected_contents("bbccd"); - VerifyStreamRead(expected_contents.c_str(), expected_contents.size(), - dir_path_.substr(0, dir_path_.size() - 1), kFilePrefix); + VerifyStreamRead(expected_contents, dir_path_.substr(0, dir_path_.size() - 1), + kFilePrefix); } // Tests that writing data greater than the total capacity of the files @@ -227,11 +228,9 @@ TEST_F(MAYBE_FileRotatingStreamTest, WriteOverflowAndRead) { std::string message("foobarbaz"); WriteAndFlush(message.c_str(), message.size()); std::string expected_file_contents("z"); - VerifyFileContents(expected_file_contents.c_str(), - expected_file_contents.size(), stream_->GetFilePath(0)); + VerifyFileContents(expected_file_contents, stream_->GetFilePath(0)); std::string expected_stream_contents("arbaz"); - VerifyStreamRead(expected_stream_contents.c_str(), - expected_stream_contents.size(), dir_path_, kFilePrefix); + VerifyStreamRead(expected_stream_contents, dir_path_, kFilePrefix); } // Tests that the returned file paths have the right folder and prefix. @@ -255,12 +254,12 @@ TEST_F(MAYBE_FileRotatingStreamTest, GetFilePath) { class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test { protected: - void Init(const std::string& dir_name, size_t max_total_log_size) { + void Init(absl::string_view dir_name, size_t max_total_log_size) { dir_path_ = webrtc::test::OutputPath(); // Append per-test output path in order to run within gtest parallel. - dir_path_.append(dir_name); - dir_path_.append(webrtc::test::kPathDelimiter); + dir_path_.append(dir_name.begin(), dir_name.end()); + dir_path_.append(std::string(webrtc::test::kPathDelimiter)); ASSERT_TRUE(webrtc::test::CreateDir(dir_path_)); stream_.reset( new CallSessionFileRotatingStream(dir_path_, max_total_log_size)); @@ -283,15 +282,16 @@ class MAYBE_CallSessionFileRotatingStreamTest : public ::testing::Test { // Checks that the stream reads in the expected contents and then returns an // end of stream result. - void VerifyStreamRead(const char* expected_contents, - const size_t expected_length, - const std::string& dir_path) { + void VerifyStreamRead(absl::string_view expected_contents, + absl::string_view dir_path) { + size_t expected_length = expected_contents.size(); CallSessionFileRotatingStreamReader reader(dir_path); EXPECT_EQ(reader.GetSize(), expected_length); std::unique_ptr buffer(new uint8_t[expected_length]); memset(buffer.get(), 0, expected_length); EXPECT_EQ(expected_length, reader.ReadAll(buffer.get(), expected_length)); - EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length)); + EXPECT_EQ(0, + memcmp(expected_contents.data(), buffer.get(), expected_length)); } std::unique_ptr stream_; @@ -307,8 +307,7 @@ TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmallest) { std::string message("abcde"); WriteAndFlush(message.c_str(), message.size()); std::string expected_contents("abe"); - VerifyStreamRead(expected_contents.c_str(), expected_contents.size(), - dir_path_); + VerifyStreamRead(expected_contents, dir_path_); } // Tests that writing and reading to a stream with capacity lesser than 4MB @@ -320,8 +319,7 @@ TEST_F(MAYBE_CallSessionFileRotatingStreamTest, WriteAndReadSmall) { std::string message("123456789"); WriteAndFlush(message.c_str(), message.size()); std::string expected_contents("1234789"); - VerifyStreamRead(expected_contents.c_str(), expected_contents.size(), - dir_path_); + VerifyStreamRead(expected_contents, dir_path_); } // Tests that writing and reading to a stream with capacity greater than 4MB diff --git a/rtc_base/format_macros.h b/rtc_base/format_macros.h deleted file mode 100644 index 83240fb501..0000000000 --- a/rtc_base/format_macros.h +++ /dev/null @@ -1,59 +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. - */ - -#ifndef RTC_BASE_FORMAT_MACROS_H_ -#define RTC_BASE_FORMAT_MACROS_H_ - -// This file defines the format macros for some integer types and is derived -// from Chromium's base/format_macros.h. - -// To print a 64-bit value in a portable way: -// int64_t value; -// printf("xyz:%" PRId64, value); -// The "d" in the macro corresponds to %d; you can also use PRIu64 etc. -// -// To print a size_t value in a portable way: -// size_t size; -// printf("xyz: %" RTC_PRIuS, size); -// The "u" in the macro corresponds to %u, and S is for "size". - -#if defined(WEBRTC_POSIX) - -#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) -#error "inttypes.h has already been included before this header file, but " -#error "without __STDC_FORMAT_MACROS defined." -#endif - -#if !defined(__STDC_FORMAT_MACROS) -#define __STDC_FORMAT_MACROS -#endif - -#include - -#include "rtc_base/system/arch.h" - -#define RTC_PRIuS "zu" - -#else // WEBRTC_WIN - -#include - -#if !defined(PRId64) || !defined(PRIu64) || !defined(PRIx64) -#error "inttypes.h provided by win toolchain should define these." -#endif - -// PRI*64 were added in MSVC 2013, while "%zu" is supported since MSVC 2015 -// (so needs to be special-cased to "%Iu" instead). - -#define RTC_PRIuS "Iu" - -#endif - -#endif // RTC_BASE_FORMAT_MACROS_H_ diff --git a/rtc_base/gunit.cc b/rtc_base/gunit.cc index 83ee8075fb..7cd60fe9ee 100644 --- a/rtc_base/gunit.cc +++ b/rtc_base/gunit.cc @@ -13,6 +13,7 @@ #include #include "absl/strings/match.h" +#include "absl/strings/string_view.h" ::testing::AssertionResult AssertStartsWith(const char* text_expr, const char* prefix_expr, @@ -30,9 +31,9 @@ ::testing::AssertionResult AssertStartsWith(const char* text_expr, ::testing::AssertionResult AssertStringContains(const char* str_expr, const char* substr_expr, - const std::string& str, - const std::string& substr) { - if (str.find(substr) != std::string::npos) { + absl::string_view str, + absl::string_view substr) { + if (str.find(substr) != absl::string_view::npos) { return ::testing::AssertionSuccess(); } else { return ::testing::AssertionFailure() diff --git a/rtc_base/gunit.h b/rtc_base/gunit.h index dedf3ee067..6bc1419729 100644 --- a/rtc_base/gunit.h +++ b/rtc_base/gunit.h @@ -11,6 +11,7 @@ #ifndef RTC_BASE_GUNIT_H_ #define RTC_BASE_GUNIT_H_ +#include "absl/strings/string_view.h" #include "rtc_base/fake_clock.h" #include "rtc_base/logging.h" #include "rtc_base/thread.h" @@ -30,11 +31,11 @@ #define WAIT_(ex, timeout, res) \ do { \ int64_t start = rtc::SystemTimeMillis(); \ - res = (ex); \ + res = (ex) && true; \ while (!res && rtc::SystemTimeMillis() < start + (timeout)) { \ rtc::Thread::Current()->ProcessMessages(0); \ rtc::Thread::Current()->SleepMs(1); \ - res = (ex); \ + res = (ex) && true; \ } \ } while (0) @@ -162,7 +163,7 @@ testing::AssertionResult AssertStartsWith(const char* text_expr, // Usage: EXPECT_PRED_FORMAT2(AssertStringContains, str, "substring"); testing::AssertionResult AssertStringContains(const char* str_expr, const char* substr_expr, - const std::string& str, - const std::string& substr); + absl::string_view str, + absl::string_view substr); #endif // RTC_BASE_GUNIT_H_ diff --git a/rtc_base/helpers.cc b/rtc_base/helpers.cc index 64cab10335..337239894a 100644 --- a/rtc_base/helpers.cc +++ b/rtc_base/helpers.cc @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" @@ -145,10 +146,8 @@ bool CreateRandomString(size_t len, std::string* str) { return CreateRandomString(len, kBase64, 64, str); } -bool CreateRandomString(size_t len, - const std::string& table, - std::string* str) { - return CreateRandomString(len, table.c_str(), static_cast(table.size()), +bool CreateRandomString(size_t len, absl::string_view table, std::string* str) { + return CreateRandomString(len, table.data(), static_cast(table.size()), str); } diff --git a/rtc_base/helpers.h b/rtc_base/helpers.h index 2fd2fc5218..c214f5212f 100644 --- a/rtc_base/helpers.h +++ b/rtc_base/helpers.h @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -42,7 +43,7 @@ RTC_EXPORT bool CreateRandomString(size_t length, std::string* str); // For ease of implementation, the function requires that the table // size evenly divide 256; otherwise, it returns false. RTC_EXPORT bool CreateRandomString(size_t length, - const std::string& table, + absl::string_view table, std::string* str); // Generates (cryptographically) random data of the given length. diff --git a/rtc_base/http_common.cc b/rtc_base/http_common.cc index 0d7832264b..2968cf12c3 100644 --- a/rtc_base/http_common.cc +++ b/rtc_base/http_common.cc @@ -10,6 +10,8 @@ #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #include @@ -118,14 +120,14 @@ const ConstantToLabel SECURITY_ERRORS[] = { typedef std::pair HttpAttribute; typedef std::vector HttpAttributeList; -inline bool IsEndOfAttributeName(size_t pos, size_t len, const char* data) { - if (pos >= len) +inline bool IsEndOfAttributeName(size_t pos, absl::string_view data) { + if (pos >= data.size()) return true; if (isspace(static_cast(data[pos]))) return true; // The reason for this complexity is that some attributes may contain trailing // equal signs (like base64 tokens in Negotiate auth headers) - if ((pos + 1 < len) && (data[pos] == '=') && + if ((pos + 1 < data.size()) && (data[pos] == '=') && !isspace(static_cast(data[pos + 1])) && (data[pos + 1] != '=')) { return true; @@ -133,10 +135,10 @@ inline bool IsEndOfAttributeName(size_t pos, size_t len, const char* data) { return false; } -void HttpParseAttributes(const char* data, - size_t len, +void HttpParseAttributes(absl::string_view data, HttpAttributeList& attributes) { size_t pos = 0; + const size_t len = data.size(); while (true) { // Skip leading whitespace while ((pos < len) && isspace(static_cast(data[pos]))) { @@ -149,12 +151,12 @@ void HttpParseAttributes(const char* data, // Find end of attribute name size_t start = pos; - while (!IsEndOfAttributeName(pos, len, data)) { + while (!IsEndOfAttributeName(pos, data)) { ++pos; } HttpAttribute attribute; - attribute.first.assign(data + start, data + pos); + attribute.first.assign(data.data() + start, data.data() + pos); // Attribute has value? if ((pos < len) && (data[pos] == '=')) { @@ -185,7 +187,7 @@ void HttpParseAttributes(const char* data, } bool HttpHasAttribute(const HttpAttributeList& attributes, - const std::string& name, + absl::string_view name, std::string* value) { for (HttpAttributeList::const_iterator it = attributes.begin(); it != attributes.end(); ++it) { @@ -213,7 +215,7 @@ bool HttpHasNthAttribute(HttpAttributeList& attributes, return true; } -std::string quote(const std::string& str) { +std::string quote(absl::string_view str) { std::string result; result.push_back('"'); for (size_t i = 0; i < str.size(); ++i) { @@ -232,7 +234,7 @@ struct NegotiateAuthContext : public HttpAuthContext { size_t steps; bool specified_credentials; - NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2) + NegotiateAuthContext(absl::string_view auth, CredHandle c1, CtxtHandle c2) : HttpAuthContext(auth), cred(c1), ctx(c2), @@ -248,18 +250,17 @@ struct NegotiateAuthContext : public HttpAuthContext { } // anonymous namespace -HttpAuthResult HttpAuthenticate(const char* challenge, - size_t len, +HttpAuthResult HttpAuthenticate(absl::string_view challenge, const SocketAddress& server, - const std::string& method, - const std::string& uri, - const std::string& username, + absl::string_view method, + absl::string_view uri, + absl::string_view username, const CryptString& password, HttpAuthContext*& context, std::string& response, std::string& auth_method) { HttpAttributeList args; - HttpParseAttributes(challenge, len, args); + HttpParseAttributes(challenge, args); HttpHasNthAttribute(args, 0, &auth_method, nullptr); if (context && (context->auth_method != auth_method)) @@ -280,7 +281,7 @@ HttpAuthResult HttpAuthenticate(const char* challenge, // std::string decoded = username + ":" + password; size_t len = username.size() + password.GetLength() + 2; char* sensitive = new char[len]; - size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + size_t pos = strcpyn(sensitive, len, username); pos += strcpyn(sensitive + pos, len - pos, ":"); password.CopyTo(sensitive + pos, true); @@ -320,13 +321,13 @@ HttpAuthResult HttpAuthenticate(const char* challenge, // std::string A1 = username + ":" + realm + ":" + password; size_t len = username.size() + realm.size() + password.GetLength() + 3; char* sensitive = new char[len]; // A1 - size_t pos = strcpyn(sensitive, len, username.data(), username.size()); + size_t pos = strcpyn(sensitive, len, username); pos += strcpyn(sensitive + pos, len - pos, ":"); - pos += strcpyn(sensitive + pos, len - pos, realm.c_str()); + pos += strcpyn(sensitive + pos, len - pos, realm); pos += strcpyn(sensitive + pos, len - pos, ":"); password.CopyTo(sensitive + pos, true); - std::string A2 = method + ":" + uri; + std::string A2 = std::string(method) + ":" + std::string(uri); std::string middle; if (has_qop) { qop = "auth"; @@ -459,11 +460,11 @@ HttpAuthResult HttpAuthenticate(const char* challenge, size_t len = password.GetLength() + 1; char* sensitive = new char[len]; password.CopyTo(sensitive, true); - std::string::size_type pos = username.find('\\'); - if (pos == std::string::npos) { + absl::string_view::size_type pos = username.find('\\'); + if (pos == absl::string_view::npos) { auth_id.UserLength = static_cast( std::min(sizeof(userbuf) - 1, username.size())); - memcpy(userbuf, username.c_str(), auth_id.UserLength); + memcpy(userbuf, username.data(), auth_id.UserLength); userbuf[auth_id.UserLength] = 0; auth_id.DomainLength = 0; domainbuf[auth_id.DomainLength] = 0; @@ -474,11 +475,11 @@ HttpAuthResult HttpAuthenticate(const char* challenge, } else { auth_id.UserLength = static_cast( std::min(sizeof(userbuf) - 1, username.size() - pos - 1)); - memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength); + memcpy(userbuf, username.data() + pos + 1, auth_id.UserLength); userbuf[auth_id.UserLength] = 0; auth_id.DomainLength = static_cast(std::min(sizeof(domainbuf) - 1, pos)); - memcpy(domainbuf, username.c_str(), auth_id.DomainLength); + memcpy(domainbuf, username.data(), auth_id.DomainLength); domainbuf[auth_id.DomainLength] = 0; auth_id.PasswordLength = static_cast( std::min(sizeof(passbuf) - 1, password.GetLength())); diff --git a/rtc_base/http_common.h b/rtc_base/http_common.h index edf161fb4c..06e42c6703 100644 --- a/rtc_base/http_common.h +++ b/rtc_base/http_common.h @@ -13,6 +13,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { class CryptString; @@ -24,7 +26,7 @@ class SocketAddress; struct HttpAuthContext { std::string auth_method; - HttpAuthContext(const std::string& auth) : auth_method(auth) {} + HttpAuthContext(absl::string_view auth) : auth_method(auth) {} virtual ~HttpAuthContext() {} }; @@ -34,12 +36,11 @@ enum HttpAuthResult { HAR_RESPONSE, HAR_IGNORE, HAR_CREDENTIALS, HAR_ERROR }; // Start by passing a null pointer, then pass the same pointer each additional // call. When the authentication attempt is finished, delete the context. // TODO(bugs.webrtc.org/8905): Change "response" to "ZeroOnFreeBuffer". -HttpAuthResult HttpAuthenticate(const char* challenge, - size_t len, +HttpAuthResult HttpAuthenticate(absl::string_view challenge, const SocketAddress& server, - const std::string& method, - const std::string& uri, - const std::string& username, + absl::string_view method, + absl::string_view uri, + absl::string_view username, const CryptString& password, HttpAuthContext*& context, std::string& response, diff --git a/rtc_base/ip_address.cc b/rtc_base/ip_address.cc index 86f42e0bf9..d544b611e1 100644 --- a/rtc_base/ip_address.cc +++ b/rtc_base/ip_address.cc @@ -11,6 +11,8 @@ #if defined(WEBRTC_POSIX) #include #include + +#include "absl/strings/string_view.h" #ifdef OPENBSD #include #endif @@ -276,14 +278,14 @@ bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out) { return false; } -bool IPFromString(const std::string& str, IPAddress* out) { +bool IPFromString(absl::string_view str, IPAddress* out) { if (!out) { return false; } in_addr addr; - if (rtc::inet_pton(AF_INET, str.c_str(), &addr) == 0) { + if (rtc::inet_pton(AF_INET, str, &addr) == 0) { in6_addr addr6; - if (rtc::inet_pton(AF_INET6, str.c_str(), &addr6) == 0) { + if (rtc::inet_pton(AF_INET6, str, &addr6) == 0) { *out = IPAddress(); return false; } @@ -294,7 +296,7 @@ bool IPFromString(const std::string& str, IPAddress* out) { return true; } -bool IPFromString(const std::string& str, int flags, InterfaceAddress* out) { +bool IPFromString(absl::string_view str, int flags, InterfaceAddress* out) { IPAddress ip; if (!IPFromString(str, &ip)) { return false; diff --git a/rtc_base/ip_address.h b/rtc_base/ip_address.h index 8725417393..58ad8ba4b2 100644 --- a/rtc_base/ip_address.h +++ b/rtc_base/ip_address.h @@ -16,6 +16,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #endif #if defined(WEBRTC_WIN) #include @@ -29,8 +31,8 @@ #if defined(WEBRTC_WIN) #include "rtc_base/win32.h" #endif +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" - namespace rtc { enum IPv6AddressFlag { @@ -155,8 +157,8 @@ class RTC_EXPORT InterfaceAddress : public IPAddress { }; bool IPFromAddrInfo(struct addrinfo* info, IPAddress* out); -RTC_EXPORT bool IPFromString(const std::string& str, IPAddress* out); -RTC_EXPORT bool IPFromString(const std::string& str, +RTC_EXPORT bool IPFromString(absl::string_view str, IPAddress* out); +RTC_EXPORT bool IPFromString(absl::string_view str, int flags, InterfaceAddress* out); bool IPIsAny(const IPAddress& ip); diff --git a/rtc_base/ip_address_unittest.cc b/rtc_base/ip_address_unittest.cc index f94649cfee..9ca05c95fe 100644 --- a/rtc_base/ip_address_unittest.cc +++ b/rtc_base/ip_address_unittest.cc @@ -10,6 +10,7 @@ #include "rtc_base/ip_address.h" +#include "absl/strings/string_view.h" #include "test/gtest.h" namespace rtc { @@ -118,7 +119,7 @@ bool AreEqual(const IPAddress& addr, const IPAddress& addr2) { return true; } -bool BrokenIPStringFails(const std::string& broken) { +bool BrokenIPStringFails(absl::string_view broken) { IPAddress addr(0); // Intentionally make it v4. if (IPFromString(kIPv4BrokenString1, &addr)) { return false; @@ -126,13 +127,13 @@ bool BrokenIPStringFails(const std::string& broken) { return addr.family() == AF_UNSPEC; } -bool CheckMaskCount(const std::string& mask, int expected_length) { +bool CheckMaskCount(absl::string_view mask, int expected_length) { IPAddress addr; return IPFromString(mask, &addr) && (expected_length == CountIPMaskBits(addr)); } -bool TryInvalidMaskCount(const std::string& mask) { +bool TryInvalidMaskCount(absl::string_view mask) { // We don't care about the result at all, but we do want to know if // CountIPMaskBits is going to crash or infinite loop or something. IPAddress addr; @@ -143,9 +144,9 @@ bool TryInvalidMaskCount(const std::string& mask) { return true; } -bool CheckTruncateIP(const std::string& initial, +bool CheckTruncateIP(absl::string_view initial, int truncate_length, - const std::string& expected_result) { + absl::string_view expected_result) { IPAddress addr, expected; IPFromString(initial, &addr); IPFromString(expected_result, &expected); diff --git a/rtc_base/log_sinks.cc b/rtc_base/log_sinks.cc index 4365142517..f511948ed3 100644 --- a/rtc_base/log_sinks.cc +++ b/rtc_base/log_sinks.cc @@ -15,12 +15,13 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" namespace rtc { -FileRotatingLogSink::FileRotatingLogSink(const std::string& log_dir_path, - const std::string& log_prefix, +FileRotatingLogSink::FileRotatingLogSink(absl::string_view log_dir_path, + absl::string_view log_prefix, size_t max_log_size, size_t num_log_files) : FileRotatingLogSink(new FileRotatingStream(log_dir_path, @@ -36,23 +37,33 @@ FileRotatingLogSink::FileRotatingLogSink(FileRotatingStream* stream) FileRotatingLogSink::~FileRotatingLogSink() {} void FileRotatingLogSink::OnLogMessage(const std::string& message) { + OnLogMessage(absl::string_view(message)); +} + +void FileRotatingLogSink::OnLogMessage(absl::string_view message) { if (!stream_->IsOpen()) { std::fprintf(stderr, "Init() must be called before adding this sink.\n"); return; } - stream_->Write(message.c_str(), message.size()); + stream_->Write(message.data(), message.size()); } void FileRotatingLogSink::OnLogMessage(const std::string& message, LoggingSeverity sev, const char* tag) { + OnLogMessage(absl::string_view(message), sev, tag); +} + +void FileRotatingLogSink::OnLogMessage(absl::string_view message, + LoggingSeverity sev, + const char* tag) { if (!stream_->IsOpen()) { std::fprintf(stderr, "Init() must be called before adding this sink.\n"); return; } stream_->Write(tag, strlen(tag)); stream_->Write(": ", 2); - stream_->Write(message.c_str(), message.size()); + stream_->Write(message.data(), message.size()); } bool FileRotatingLogSink::Init() { @@ -64,7 +75,7 @@ bool FileRotatingLogSink::DisableBuffering() { } CallSessionFileRotatingLogSink::CallSessionFileRotatingLogSink( - const std::string& log_dir_path, + absl::string_view log_dir_path, size_t max_total_log_size) : FileRotatingLogSink( new CallSessionFileRotatingStream(log_dir_path, max_total_log_size)) { diff --git a/rtc_base/log_sinks.h b/rtc_base/log_sinks.h index d2c286a86a..62a93b85a8 100644 --- a/rtc_base/log_sinks.h +++ b/rtc_base/log_sinks.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/file_rotating_stream.h" #include "rtc_base/logging.h" @@ -27,8 +28,8 @@ class FileRotatingLogSink : public LogSink { public: // `num_log_files` must be greater than 1 and `max_log_size` must be greater // than 0. - FileRotatingLogSink(const std::string& log_dir_path, - const std::string& log_prefix, + FileRotatingLogSink(absl::string_view log_dir_path, + absl::string_view log_prefix, size_t max_log_size, size_t num_log_files); ~FileRotatingLogSink() override; @@ -39,9 +40,13 @@ class FileRotatingLogSink : public LogSink { // Writes the message to the current file. It will spill over to the next // file if needed. void OnLogMessage(const std::string& message) override; + void OnLogMessage(absl::string_view message) override; void OnLogMessage(const std::string& message, LoggingSeverity sev, const char* tag) override; + void OnLogMessage(absl::string_view message, + LoggingSeverity sev, + const char* tag) override; // Deletes any existing files in the directory and creates a new log file. virtual bool Init(); @@ -60,7 +65,7 @@ class FileRotatingLogSink : public LogSink { // Init() must be called before adding this sink. class CallSessionFileRotatingLogSink : public FileRotatingLogSink { public: - CallSessionFileRotatingLogSink(const std::string& log_dir_path, + CallSessionFileRotatingLogSink(absl::string_view log_dir_path, size_t max_total_log_size); ~CallSessionFileRotatingLogSink() override; diff --git a/rtc_base/logging.cc b/rtc_base/logging.cc index 2b5c80b33c..b93474de3a 100644 --- a/rtc_base/logging.cc +++ b/rtc_base/logging.cc @@ -42,6 +42,7 @@ static const int kMaxLogLineSize = 1024 - 60; #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/platform_thread_types.h" #include "rtc_base/string_encode.h" @@ -187,7 +188,7 @@ LogMessage::LogMessage(const char* file, LogMessage::LogMessage(const char* file, int line, LoggingSeverity sev, - const std::string& tag) + absl::string_view tag) : LogMessage(file, line, sev) { print_stream_ << tag << ": "; } @@ -295,7 +296,7 @@ void LogMessage::RemoveLogToStream(LogSink* stream) { UpdateMinLogSeverity(); } -void LogMessage::ConfigureLogging(const char* params) { +void LogMessage::ConfigureLogging(absl::string_view params) { LoggingSeverity current_level = LS_VERBOSE; LoggingSeverity debug_level = GetLogToDebug(); @@ -354,13 +355,14 @@ void LogMessage::UpdateMinLogSeverity() } #if defined(WEBRTC_ANDROID) -void LogMessage::OutputToDebug(const std::string& str, +void LogMessage::OutputToDebug(absl::string_view str, LoggingSeverity severity, const char* tag) { #else -void LogMessage::OutputToDebug(const std::string& str, +void LogMessage::OutputToDebug(absl::string_view str, LoggingSeverity severity) { #endif + std::string str_str = std::string(str); bool log_to_stderr = log_to_stderr_; #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG) // On the Mac, all stderr output goes to the Console log and causes clutter. @@ -385,7 +387,7 @@ void LogMessage::OutputToDebug(const std::string& str, #if defined(WEBRTC_WIN) // Always log to the debugger. // Perhaps stderr should be controlled by a preference, as on Mac? - OutputDebugStringA(str.c_str()); + OutputDebugStringA(str_str.c_str()); if (log_to_stderr) { // This handles dynamically allocated consoles, too. if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) { @@ -425,14 +427,14 @@ void LogMessage::OutputToDebug(const std::string& str, int idx = 0; const int max_lines = size / kMaxLogLineSize + 1; if (max_lines == 1) { - __android_log_print(prio, tag, "%.*s", size, str.c_str()); + __android_log_print(prio, tag, "%.*s", size, str_str.c_str()); } else { while (size > 0) { const int len = std::min(size, kMaxLogLineSize); // Use the size of the string in the format (str may have \0 in the // middle). __android_log_print(prio, tag, "[%d/%d] %.*s", line + 1, max_lines, len, - str.c_str() + idx); + str_str.c_str() + idx); idx += len; size -= len; ++line; @@ -440,7 +442,7 @@ void LogMessage::OutputToDebug(const std::string& str, } #endif // WEBRTC_ANDROID if (log_to_stderr) { - fprintf(stderr, "%s", str.c_str()); + fprintf(stderr, "%s", str_str.c_str()); fflush(stderr); } } @@ -563,4 +565,20 @@ void LogSink::OnLogMessage(const std::string& msg, LoggingSeverity /* severity */) { OnLogMessage(msg); } + +// Inefficient default implementation, override is recommended. +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag) { + OnLogMessage(tag + (": " + std::string(msg)), severity); +} + +void LogSink::OnLogMessage(absl::string_view msg, + LoggingSeverity /* severity */) { + OnLogMessage(msg); +} + +void LogSink::OnLogMessage(absl::string_view msg) { + OnLogMessage(std::string(msg)); +} } // namespace rtc diff --git a/rtc_base/logging.h b/rtc_base/logging.h index eaa84e893a..13738f0d0e 100644 --- a/rtc_base/logging.h +++ b/rtc_base/logging.h @@ -49,6 +49,7 @@ #include #include // no-presubmit-check TODO(webrtc:8982) #include +#include #include #include "absl/base/attributes.h" @@ -72,9 +73,7 @@ namespace rtc { ////////////////////////////////////////////////////////////////////// - -// Note that the non-standard LoggingSeverity aliases exist because they are -// still in broad use. The meanings of the levels are: +// The meanings of the levels are: // LS_VERBOSE: This level is for data which we do not want to appear in the // normal debug log, but should appear in diagnostic logs. // LS_INFO: Chatty level used in debugging for all sorts of things, the default @@ -114,6 +113,13 @@ class LogSink { LoggingSeverity severity); virtual void OnLogMessage(const std::string& message) = 0; + virtual void OnLogMessage(absl::string_view msg, + LoggingSeverity severity, + const char* tag); + virtual void OnLogMessage(absl::string_view message, + LoggingSeverity severity); + virtual void OnLogMessage(absl::string_view message); + private: friend class ::rtc::LogMessage; #if RTC_LOG_ENABLED() @@ -272,8 +278,15 @@ inline Val MakeVal( template struct has_to_log_string : std::false_type {}; template -struct has_to_log_string()))> - : std::true_type {}; +struct has_to_log_string())), + std::string>::value>> : std::true_type {}; + +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {ToLogString(x)}; +} // Handle arbitrary types other than the above by falling back to stringstream. // TODO(bugs.webrtc.org/9278): Get rid of this overload when callers don't need @@ -295,11 +308,6 @@ ToStringVal MakeVal(const T& x) { return {os.str()}; } -template ::value>* = nullptr> -ToStringVal MakeVal(const T& x) { - return {ToLogString(x)}; -} - #if RTC_LOG_ENABLED() void Log(const LogArgType* fmt, ...); #else @@ -434,7 +442,7 @@ class LogMessage { LogMessage(const char* file, int line, LoggingSeverity sev, - const std::string& tag); + absl::string_view tag); ~LogMessage(); LogMessage(const LogMessage&) = delete; @@ -480,7 +488,7 @@ class LogMessage { static int GetMinLogSeverity(); // Parses the provided parameter stream to configure the options above. // Useful for configuring logging from the command line. - static void ConfigureLogging(const char* params); + static void ConfigureLogging(absl::string_view params); // Checks the current global debug severity and if the `streams_` collection // is empty. If `severity` is smaller than the global severity and if the // `streams_` collection is empty, the LogMessage will be considered a noop @@ -511,7 +519,7 @@ class LogMessage { LogMessage(const char* file, int line, LoggingSeverity sev, - const std::string& tag) {} + absl::string_view tag) {} ~LogMessage() = default; inline void AddTag(const char* tag) {} @@ -529,7 +537,7 @@ class LogMessage { inline static void RemoveLogToStream(LogSink* stream) {} inline static int GetLogToStream(LogSink* stream = nullptr) { return 0; } inline static int GetMinLogSeverity() { return 0; } - inline static void ConfigureLogging(const char* params) {} + inline static void ConfigureLogging(absl::string_view params) {} static constexpr bool IsNoop(LoggingSeverity severity) { return true; } template static constexpr bool IsNoop() { @@ -546,11 +554,11 @@ class LogMessage { // These write out the actual log messages. #if defined(WEBRTC_ANDROID) - static void OutputToDebug(const std::string& msg, + static void OutputToDebug(absl::string_view msg, LoggingSeverity severity, const char* tag); #else - static void OutputToDebug(const std::string& msg, LoggingSeverity severity); + static void OutputToDebug(absl::string_view msg, LoggingSeverity severity); #endif // defined(WEBRTC_ANDROID) // Called from the dtor (or from a test) to append optional extra error @@ -587,11 +595,11 @@ class LogMessage { // Next methods do nothing; no one will call these functions. inline static void UpdateMinLogSeverity() {} #if defined(WEBRTC_ANDROID) - inline static void OutputToDebug(const std::string& msg, + inline static void OutputToDebug(absl::string_view msg, LoggingSeverity severity, const char* tag) {} #else - inline static void OutputToDebug(const std::string& msg, + inline static void OutputToDebug(absl::string_view msg, LoggingSeverity severity) {} #endif // defined(WEBRTC_ANDROID) inline void FinishPrintStream() {} diff --git a/rtc_base/logging_unittest.cc b/rtc_base/logging_unittest.cc index dc1208f3f6..c267778951 100644 --- a/rtc_base/logging_unittest.cc +++ b/rtc_base/logging_unittest.cc @@ -16,11 +16,13 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" #include "rtc_base/event.h" #include "rtc_base/platform_thread.h" #include "rtc_base/time_utils.h" +#include "test/gmock.h" #include "test/gtest.h" namespace rtc { @@ -34,7 +36,10 @@ class LogSinkImpl : public LogSink { private: void OnLogMessage(const std::string& message) override { - log_data_->append(message); + OnLogMessage(absl::string_view(message)); + } + void OnLogMessage(absl::string_view message) override { + log_data_->append(message.begin(), message.end()); } std::string* const log_data_; }; @@ -295,5 +300,20 @@ TEST(LogTest, NoopSeverityDoesNotRunStringFormatting) { EXPECT_FALSE(was_called); } +struct TestStruct {}; +std::string ToLogString(TestStruct foo) { + return "bar"; +} + +TEST(LogTest, ToLogStringUsedForUnknownTypes) { + std::string str; + LogSinkImpl stream(&str); + LogMessage::AddLogToStream(&stream, LS_INFO); + TestStruct t; + RTC_LOG(LS_INFO) << t; + EXPECT_THAT(str, ::testing::HasSubstr("bar")); + LogMessage::RemoveLogToStream(&stream); +} + } // namespace rtc #endif // RTC_LOG_ENABLED() diff --git a/rtc_base/mdns_responder_interface.h b/rtc_base/mdns_responder_interface.h index 64fb3cebff..14ef9a202d 100644 --- a/rtc_base/mdns_responder_interface.h +++ b/rtc_base/mdns_responder_interface.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ip_address.h" namespace webrtc { @@ -23,7 +24,7 @@ namespace webrtc { class MdnsResponderInterface { public: using NameCreatedCallback = - std::function; + std::function; using NameRemovedCallback = std::function; MdnsResponderInterface() = default; diff --git a/rtc_base/memory/BUILD.gn b/rtc_base/memory/BUILD.gn index ee66ac0df8..8965a2c702 100644 --- a/rtc_base/memory/BUILD.gn +++ b/rtc_base/memory/BUILD.gn @@ -45,11 +45,18 @@ rtc_library("unittests") { testonly = true sources = [ "aligned_malloc_unittest.cc", + "always_valid_pointer_unittest.cc", "fifo_buffer_unittest.cc", ] deps = [ ":aligned_malloc", + ":always_valid_pointer", ":fifo_buffer", "../../test:test_support", ] } + +rtc_source_set("always_valid_pointer") { + sources = [ "always_valid_pointer.h" ] + deps = [ "..:checks" ] +} diff --git a/rtc_base/memory/always_valid_pointer.h b/rtc_base/memory/always_valid_pointer.h new file mode 100644 index 0000000000..db7d0a1cb4 --- /dev/null +++ b/rtc_base/memory/always_valid_pointer.h @@ -0,0 +1,110 @@ +/* + * Copyright 2022 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_MEMORY_ALWAYS_VALID_POINTER_H_ +#define RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ + +#include +#include + +#include "rtc_base/checks.h" + +namespace webrtc { + +// This template allows the instantiation of a pointer to Interface in such a +// way that if it is passed a null pointer, an object of class Default will be +// created, which will be deallocated when the pointer is deleted. +template +class AlwaysValidPointer { + public: + explicit AlwaysValidPointer(Interface* pointer) + : owned_instance_(pointer ? nullptr : std::make_unique()), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + template ::value), + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Arg arg) + : owned_instance_(pointer ? nullptr + : std::make_unique(std::move(arg))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Multiple arguments + template + AlwaysValidPointer(Interface* pointer, Arg1 arg1, Args... args) + : owned_instance_(pointer + ? nullptr + : std::make_unique(std::move(arg1), + std::move(args...))), + pointer_(pointer ? pointer : owned_instance_.get()) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) using |pointer|, without taking ownership + // b) calling |function| and taking ownership of the result + template ::value, + bool>::type = true> + AlwaysValidPointer(Interface* pointer, Func function) + : owned_instance_(pointer ? nullptr : function()), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default. + AlwaysValidPointer(std::unique_ptr&& instance, Interface* pointer) + : owned_instance_( + instance + ? std::move(instance) + : (pointer == nullptr ? std::make_unique() : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + // Create a pointer by + // a) taking over ownership of |instance| + // b) or fallback to |pointer|, without taking ownership. + // c) or Default (with forwarded args). + template + AlwaysValidPointer(std::unique_ptr&& instance, + Interface* pointer, + Args... args) + : owned_instance_( + instance ? std::move(instance) + : (pointer == nullptr + ? std::make_unique(std::move(args...)) + : nullptr)), + pointer_(owned_instance_ ? owned_instance_.get() : pointer) { + RTC_DCHECK(pointer_); + } + + Interface* get() { return pointer_; } + Interface* operator->() { return pointer_; } + Interface& operator*() { return *pointer_; } + + Interface* get() const { return pointer_; } + Interface* operator->() const { return pointer_; } + Interface& operator*() const { return *pointer_; } + + private: + const std::unique_ptr owned_instance_; + Interface* const pointer_; +}; + +} // namespace webrtc + +#endif // RTC_BASE_MEMORY_ALWAYS_VALID_POINTER_H_ diff --git a/rtc_base/memory/always_valid_pointer_unittest.cc b/rtc_base/memory/always_valid_pointer_unittest.cc new file mode 100644 index 0000000000..30110c7969 --- /dev/null +++ b/rtc_base/memory/always_valid_pointer_unittest.cc @@ -0,0 +1,93 @@ +/* + * 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/memory/always_valid_pointer.h" + +#include + +#include "test/gtest.h" + +namespace webrtc { + +TEST(AlwaysValidPointerTest, DefaultToEmptyValue) { + AlwaysValidPointer ptr(nullptr); + EXPECT_EQ(*ptr, ""); +} +TEST(AlwaysValidPointerTest, DefaultWithForwardedArgument) { + AlwaysValidPointer ptr(nullptr, "test"); + EXPECT_EQ(*ptr, "test"); +} +TEST(AlwaysValidPointerTest, DefaultToSubclass) { + struct A { + virtual ~A() {} + virtual int f() = 0; + }; + struct B : public A { + int b = 0; + explicit B(int val) : b(val) {} + virtual ~B() {} + int f() override { return b; } + }; + AlwaysValidPointer ptr(nullptr, 3); + EXPECT_EQ(ptr->f(), 3); + EXPECT_EQ((*ptr).f(), 3); + EXPECT_EQ(ptr.get()->f(), 3); +} +TEST(AlwaysValidPointerTest, NonDefaultValue) { + std::string str("keso"); + AlwaysValidPointer ptr(&str, "test"); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipOfInstance) { + std::string str("keso"); + std::unique_ptr str2 = std::make_unique("kent"); + AlwaysValidPointer ptr(std::move(str2), &str); + EXPECT_EQ(*ptr, "kent"); + EXPECT_EQ(str2, nullptr); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipFallbackOnPointer) { + std::string str("keso"); + std::unique_ptr str2; + AlwaysValidPointer ptr(std::move(str2), &str); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipFallbackOnDefault) { + std::unique_ptr str; + std::string* str_ptr = nullptr; + AlwaysValidPointer ptr(std::move(str), str_ptr); + EXPECT_EQ(*ptr, ""); +} + +TEST(AlwaysValidPointerTest, + TakeOverOwnershipFallbackOnDefaultWithForwardedArgument) { + std::unique_ptr str2; + AlwaysValidPointer ptr(std::move(str2), nullptr, "keso"); + EXPECT_EQ(*ptr, "keso"); +} + +TEST(AlwaysValidPointerTest, TakeOverOwnershipDoesNotForwardDefaultArguments) { + std::unique_ptr str = std::make_unique("kalle"); + std::unique_ptr str2 = std::make_unique("anka"); + AlwaysValidPointer ptr(std::move(str), nullptr, *str2); + EXPECT_EQ(*ptr, "kalle"); + EXPECT_TRUE(!str); + EXPECT_EQ(*str2, "anka"); +} + +TEST(AlwaysValidPointerTest, DefaultToLambda) { + AlwaysValidPointer ptr( + nullptr, []() { return std::make_unique("onkel skrue"); }); + EXPECT_EQ(*ptr, "onkel skrue"); +} + +} // namespace webrtc diff --git a/rtc_base/message_digest.cc b/rtc_base/message_digest.cc index 62b4a6bc97..56abcd2c7b 100644 --- a/rtc_base/message_digest.cc +++ b/rtc_base/message_digest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/openssl_digest.h" #include "rtc_base/string_encode.h" @@ -30,7 +31,7 @@ const char DIGEST_SHA_512[] = "sha-512"; static const size_t kBlockSize = 64; // valid for SHA-256 and down -MessageDigest* MessageDigestFactory::Create(const std::string& alg) { +MessageDigest* MessageDigestFactory::Create(absl::string_view alg) { MessageDigest* digest = new OpenSSLDigest(alg); if (digest->Size() == 0) { // invalid algorithm delete digest; @@ -39,7 +40,7 @@ MessageDigest* MessageDigestFactory::Create(const std::string& alg) { return digest; } -bool IsFips180DigestAlgorithm(const std::string& alg) { +bool IsFips180DigestAlgorithm(absl::string_view alg) { // These are the FIPS 180 algorithms. According to RFC 4572 Section 5, // "Self-signed certificates (for which legacy certificates are not a // consideration) MUST use one of the FIPS 180 algorithms (SHA-1, @@ -59,7 +60,7 @@ size_t ComputeDigest(MessageDigest* digest, return digest->Finish(output, out_len); } -size_t ComputeDigest(const std::string& alg, +size_t ComputeDigest(absl::string_view alg, const void* input, size_t in_len, void* output, @@ -69,15 +70,15 @@ size_t ComputeDigest(const std::string& alg, : 0; } -std::string ComputeDigest(MessageDigest* digest, const std::string& input) { +std::string ComputeDigest(MessageDigest* digest, absl::string_view input) { std::unique_ptr output(new char[digest->Size()]); ComputeDigest(digest, input.data(), input.size(), output.get(), digest->Size()); - return hex_encode(output.get(), digest->Size()); + return hex_encode(absl::string_view(output.get(), digest->Size())); } -bool ComputeDigest(const std::string& alg, - const std::string& input, +bool ComputeDigest(absl::string_view alg, + absl::string_view input, std::string* output) { std::unique_ptr digest(MessageDigestFactory::Create(alg)); if (!digest) { @@ -87,7 +88,7 @@ bool ComputeDigest(const std::string& alg, return true; } -std::string ComputeDigest(const std::string& alg, const std::string& input) { +std::string ComputeDigest(absl::string_view alg, absl::string_view input) { std::string output; ComputeDigest(alg, input, &output); return output; @@ -135,7 +136,7 @@ size_t ComputeHmac(MessageDigest* digest, return digest->Finish(output, out_len); } -size_t ComputeHmac(const std::string& alg, +size_t ComputeHmac(absl::string_view alg, const void* key, size_t key_len, const void* input, @@ -151,17 +152,17 @@ size_t ComputeHmac(const std::string& alg, } std::string ComputeHmac(MessageDigest* digest, - const std::string& key, - const std::string& input) { + absl::string_view key, + absl::string_view input) { std::unique_ptr output(new char[digest->Size()]); ComputeHmac(digest, key.data(), key.size(), input.data(), input.size(), output.get(), digest->Size()); - return hex_encode(output.get(), digest->Size()); + return hex_encode(absl::string_view(output.get(), digest->Size())); } -bool ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input, +bool ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input, std::string* output) { std::unique_ptr digest(MessageDigestFactory::Create(alg)); if (!digest) { @@ -171,9 +172,9 @@ bool ComputeHmac(const std::string& alg, return true; } -std::string ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input) { +std::string ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input) { std::string output; ComputeHmac(alg, key, input, &output); return output; diff --git a/rtc_base/message_digest.h b/rtc_base/message_digest.h index 02e0bfd561..632b9af075 100644 --- a/rtc_base/message_digest.h +++ b/rtc_base/message_digest.h @@ -15,6 +15,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // Definitions for the digest algorithms. @@ -42,12 +44,12 @@ class MessageDigest { // A factory class for creating digest objects. class MessageDigestFactory { public: - static MessageDigest* Create(const std::string& alg); + static MessageDigest* Create(absl::string_view alg); }; // A check that an algorithm is in a list of approved digest algorithms // from RFC 4572 (FIPS 180). -bool IsFips180DigestAlgorithm(const std::string& alg); +bool IsFips180DigestAlgorithm(absl::string_view alg); // Functions to create hashes. @@ -63,25 +65,25 @@ size_t ComputeDigest(MessageDigest* digest, // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no // digest with the given name. -size_t ComputeDigest(const std::string& alg, +size_t ComputeDigest(absl::string_view alg, const void* input, size_t in_len, void* output, size_t out_len); // Computes the hash of `input` using the `digest` hash implementation, and // returns it as a hex-encoded string. -std::string ComputeDigest(MessageDigest* digest, const std::string& input); +std::string ComputeDigest(MessageDigest* digest, absl::string_view input); // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if // there is no digest with the given name. -std::string ComputeDigest(const std::string& alg, const std::string& input); +std::string ComputeDigest(absl::string_view alg, absl::string_view input); // Like the previous function, but returns an explicit result code. -bool ComputeDigest(const std::string& alg, - const std::string& input, +bool ComputeDigest(absl::string_view alg, + absl::string_view input, std::string* output); // Shorthand way to compute a hex-encoded hash using MD5. -inline std::string MD5(const std::string& input) { +inline std::string MD5(absl::string_view input) { return ComputeDigest(DIGEST_MD5, input); } @@ -102,7 +104,7 @@ size_t ComputeHmac(MessageDigest* digest, // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns 0 if there is no // digest with the given name. -size_t ComputeHmac(const std::string& alg, +size_t ComputeHmac(absl::string_view alg, const void* key, size_t key_len, const void* input, @@ -112,18 +114,18 @@ size_t ComputeHmac(const std::string& alg, // Computes the HMAC of `input` using the `digest` hash implementation and `key` // to key the HMAC, and returns it as a hex-encoded string. std::string ComputeHmac(MessageDigest* digest, - const std::string& key, - const std::string& input); + absl::string_view key, + absl::string_view input); // Like the previous function, but creates a digest implementation based on // the desired digest name `alg`, e.g. DIGEST_SHA_1. Returns empty string if // there is no digest with the given name. -std::string ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input); +std::string ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input); // Like the previous function, but returns an explicit result code. -bool ComputeHmac(const std::string& alg, - const std::string& key, - const std::string& input, +bool ComputeHmac(absl::string_view alg, + absl::string_view key, + absl::string_view input, std::string* output); } // namespace rtc diff --git a/rtc_base/message_digest_unittest.cc b/rtc_base/message_digest_unittest.cc index 85e9bbde53..b296783d4e 100644 --- a/rtc_base/message_digest_unittest.cc +++ b/rtc_base/message_digest_unittest.cc @@ -10,6 +10,7 @@ #include "rtc_base/message_digest.h" +#include "absl/strings/string_view.h" #include "rtc_base/string_encode.h" #include "test/gtest.h" @@ -29,7 +30,7 @@ TEST(MessageDigestTest, TestMd5Digest) { EXPECT_EQ(sizeof(output), ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output))); EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", - hex_encode(output, sizeof(output))); + hex_encode(absl::string_view(output, sizeof(output)))); EXPECT_EQ(0U, ComputeDigest(DIGEST_MD5, "abc", 3, output, sizeof(output) - 1)); } @@ -51,7 +52,7 @@ TEST(MessageDigestTest, TestSha1Digest) { EXPECT_EQ(sizeof(output), ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output))); EXPECT_EQ("a9993e364706816aba3e25717850c26c9cd0d89d", - hex_encode(output, sizeof(output))); + hex_encode(absl::string_view(output, sizeof(output)))); EXPECT_EQ(0U, ComputeDigest(DIGEST_SHA_1, "abc", 3, output, sizeof(output) - 1)); } @@ -99,7 +100,7 @@ TEST(MessageDigestTest, TestMd5Hmac) { ComputeHmac(DIGEST_MD5, key.c_str(), key.size(), input.c_str(), input.size(), output, sizeof(output))); EXPECT_EQ("9294727a3638bb1c13f48ef8158bfc9d", - hex_encode(output, sizeof(output))); + hex_encode(absl::string_view(output, sizeof(output)))); EXPECT_EQ(0U, ComputeHmac(DIGEST_MD5, key.c_str(), key.size(), input.c_str(), input.size(), output, sizeof(output) - 1)); } @@ -140,7 +141,7 @@ TEST(MessageDigestTest, TestSha1Hmac) { ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(), input.c_str(), input.size(), output, sizeof(output))); EXPECT_EQ("b617318655057264e28bc0b6fb378c8ef146be00", - hex_encode(output, sizeof(output))); + hex_encode(absl::string_view(output, sizeof(output)))); EXPECT_EQ(0U, ComputeHmac(DIGEST_SHA_1, key.c_str(), key.size(), input.c_str(), input.size(), output, sizeof(output) - 1)); diff --git a/rtc_base/nat_unittest.cc b/rtc_base/nat_unittest.cc index 2e41684b78..f7f2fb4ac5 100644 --- a/rtc_base/nat_unittest.cc +++ b/rtc_base/nat_unittest.cc @@ -37,6 +37,7 @@ #include "rtc_base/thread.h" #include "rtc_base/virtual_socket_server.h" #include "test/gtest.h" +#include "test/scoped_key_value_config.h" namespace rtc { namespace { @@ -219,16 +220,16 @@ bool TestConnectivity(const SocketAddress& src, const IPAddress& dst) { } void TestPhysicalInternal(const SocketAddress& int_addr) { + webrtc::test::ScopedKeyValueConfig field_trials; PhysicalSocketServer socket_server; - BasicNetworkManager network_manager(nullptr, &socket_server); + BasicNetworkManager network_manager(nullptr, &socket_server, &field_trials); network_manager.StartUpdating(); // Process pending messages so the network list is updated. Thread::Current()->ProcessMessages(0); - std::vector networks; - network_manager.GetNetworks(&networks); + std::vector networks = network_manager.GetNetworks(); networks.erase(std::remove_if(networks.begin(), networks.end(), - [](rtc::Network* network) { + [](const rtc::Network* network) { return rtc::kDefaultNetworkIgnoreMask & network->type(); }), @@ -242,9 +243,8 @@ void TestPhysicalInternal(const SocketAddress& int_addr) { SocketAddress ext_addr2; // Find an available IP with matching family. The test breaks if int_addr // can't talk to ip, so check for connectivity as well. - for (std::vector::iterator it = networks.begin(); - it != networks.end(); ++it) { - const IPAddress& ip = (*it)->GetBestIP(); + for (const Network* const network : networks) { + const IPAddress& ip = network->GetBestIP(); if (ip.family() == int_addr.family() && TestConnectivity(int_addr, ip)) { ext_addr2.SetIP(ip); break; diff --git a/rtc_base/net_helper.cc b/rtc_base/net_helper.cc index 893b500d56..4afee7bfb0 100644 --- a/rtc_base/net_helper.cc +++ b/rtc_base/net_helper.cc @@ -10,6 +10,8 @@ #include "rtc_base/net_helper.h" +#include "absl/strings/string_view.h" + namespace cricket { const char UDP_PROTOCOL_NAME[] = "udp"; @@ -17,7 +19,7 @@ const char TCP_PROTOCOL_NAME[] = "tcp"; const char SSLTCP_PROTOCOL_NAME[] = "ssltcp"; const char TLS_PROTOCOL_NAME[] = "tls"; -int GetProtocolOverhead(const std::string& protocol) { +int GetProtocolOverhead(absl::string_view protocol) { if (protocol == TCP_PROTOCOL_NAME || protocol == SSLTCP_PROTOCOL_NAME) { return kTcpHeaderSize; } else if (protocol == UDP_PROTOCOL_NAME) { diff --git a/rtc_base/net_helper.h b/rtc_base/net_helper.h index 9abbbdefb2..758c0faad9 100644 --- a/rtc_base/net_helper.h +++ b/rtc_base/net_helper.h @@ -12,6 +12,8 @@ #include +#include "absl/strings/string_view.h" + // This header contains helper functions and constants used by different types // of transports. namespace cricket { @@ -25,7 +27,7 @@ constexpr int kTcpHeaderSize = 20; constexpr int kUdpHeaderSize = 8; // Get the transport layer overhead per packet based on the protocol. -int GetProtocolOverhead(const std::string& protocol); +int GetProtocolOverhead(absl::string_view protocol); } // namespace cricket diff --git a/rtc_base/net_helpers.cc b/rtc_base/net_helpers.cc index f521f0f64b..f092989b43 100644 --- a/rtc_base/net_helpers.cc +++ b/rtc_base/net_helpers.cc @@ -11,6 +11,9 @@ #include "rtc_base/net_helpers.h" #include +#include + +#include "absl/strings/string_view.h" #if defined(WEBRTC_WIN) #include @@ -37,11 +40,12 @@ const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { #endif } -int inet_pton(int af, const char* src, void* dst) { +int inet_pton(int af, absl::string_view src, void* dst) { + std::string src_str = std::string(src); #if defined(WEBRTC_WIN) - return win32_inet_pton(af, src, dst); + return win32_inet_pton(af, src_str.c_str(), dst); #else - return ::inet_pton(af, src, dst); + return ::inet_pton(af, src_str.c_str(), dst); #endif } diff --git a/rtc_base/net_helpers.h b/rtc_base/net_helpers.h index 4ed84786b3..631c6348a7 100644 --- a/rtc_base/net_helpers.h +++ b/rtc_base/net_helpers.h @@ -19,12 +19,14 @@ #include "rtc_base/win32.h" #endif +#include "absl/strings/string_view.h" + namespace rtc { // 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); -int inet_pton(int af, const char* src, void* dst); +int inet_pton(int af, absl::string_view src, void* dst); bool HasIPv4Enabled(); bool HasIPv6Enabled(); diff --git a/rtc_base/network.cc b/rtc_base/network.cc index 295d39c5d9..c6442b1b63 100644 --- a/rtc_base/network.cc +++ b/rtc_base/network.cc @@ -10,6 +10,8 @@ #include "rtc_base/network.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_POSIX) #include #endif // WEBRTC_POSIX @@ -25,10 +27,13 @@ #include #include "absl/algorithm/container.h" +#include "absl/memory/memory.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" +#include "api/transport/field_trial_based_config.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" +#include "rtc_base/memory/always_valid_pointer.h" #include "rtc_base/network_monitor.h" #include "rtc_base/socket.h" // includes something that makes windows happy #include "rtc_base/string_encode.h" @@ -36,7 +41,6 @@ #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/thread.h" -#include "system_wrappers/include/field_trial.h" namespace rtc { namespace { @@ -54,19 +58,10 @@ const int kNetworksUpdateIntervalMs = 2000; const int kHighestNetworkPreference = 127; -typedef struct { - Network* net; +struct AddressList { + std::unique_ptr net; std::vector ips; -} AddressList; - -bool CompareNetworks(const Network* a, const Network* b) { - if (a->prefix_length() == b->prefix_length()) { - if (a->name() == b->name()) { - return a->prefix() < b->prefix(); - } - } - return a->name() < b->name(); -} +}; bool SortNetworks(const Network* a, const Network* b) { // Network types will be preferred above everything else while sorting @@ -190,7 +185,20 @@ const char kPublicIPv4Host[] = "8.8.8.8"; const char kPublicIPv6Host[] = "2001:4860:4860::8888"; const int kPublicPort = 53; // DNS port. -std::string MakeNetworkKey(const std::string& name, +namespace webrtc_network_internal { +bool CompareNetworks(const std::unique_ptr& a, + const std::unique_ptr& b) { + if (a->prefix_length() != b->prefix_length()) { + return a->prefix_length() < b->prefix_length(); + } + if (a->name() != b->name()) { + return a->name() < b->name(); + } + return a->prefix() < b->prefix(); +} +} // namespace webrtc_network_internal + +std::string MakeNetworkKey(absl::string_view name, const IPAddress& prefix, int prefix_length) { rtc::StringBuilder ost; @@ -213,7 +221,7 @@ bool MatchTypeNameWithIndexPattern(absl::string_view network_name, // result of the downstream network filtering, see e.g. // BasicPortAllocatorSession::GetNetworks when // PORTALLOCATOR_DISABLE_COSTLY_NETWORKS is turned on. -AdapterType GetAdapterTypeFromName(const char* network_name) { +AdapterType GetAdapterTypeFromName(absl::string_view network_name) { if (MatchTypeNameWithIndexPattern(network_name, "lo")) { // Note that we have a more robust way to determine if a network interface // is a loopback interface by checking the flag IFF_LOOPBACK in ifa_flags of @@ -261,10 +269,6 @@ AdapterType GetAdapterTypeFromName(const char* network_name) { return ADAPTER_TYPE_UNKNOWN; } -NetworkManager::NetworkManager() {} - -NetworkManager::~NetworkManager() {} - NetworkManager::EnumerationPermission NetworkManager::enumeration_permission() const { return ENUMERATION_ALLOWED; @@ -278,84 +282,83 @@ webrtc::MdnsResponderInterface* NetworkManager::GetMdnsResponder() const { return nullptr; } -NetworkManagerBase::NetworkManagerBase() +NetworkManagerBase::NetworkManagerBase( + const webrtc::FieldTrialsView* field_trials) : enumeration_permission_(NetworkManager::ENUMERATION_ALLOWED), - signal_network_preference_change_(webrtc::field_trial::IsEnabled( - "WebRTC-SignalNetworkPreferenceChange")) {} - -NetworkManagerBase::~NetworkManagerBase() { - for (const auto& kv : networks_map_) { - delete kv.second; - } -} + signal_network_preference_change_( + field_trials + ? field_trials->IsEnabled("WebRTC-SignalNetworkPreferenceChange") + : false) {} NetworkManager::EnumerationPermission NetworkManagerBase::enumeration_permission() const { return enumeration_permission_; } -void NetworkManagerBase::GetAnyAddressNetworks(NetworkList* networks) { +std::vector NetworkManagerBase::GetAnyAddressNetworks() { + std::vector networks; if (!ipv4_any_address_network_) { const rtc::IPAddress ipv4_any_address(INADDR_ANY); - ipv4_any_address_network_.reset( - new rtc::Network("any", "any", ipv4_any_address, 0, ADAPTER_TYPE_ANY)); + ipv4_any_address_network_ = std::make_unique( + "any", "any", ipv4_any_address, 0, ADAPTER_TYPE_ANY); ipv4_any_address_network_->set_default_local_address_provider(this); ipv4_any_address_network_->set_mdns_responder_provider(this); ipv4_any_address_network_->AddIP(ipv4_any_address); } - networks->push_back(ipv4_any_address_network_.get()); + networks.push_back(ipv4_any_address_network_.get()); if (!ipv6_any_address_network_) { const rtc::IPAddress ipv6_any_address(in6addr_any); - ipv6_any_address_network_.reset( - new rtc::Network("any", "any", ipv6_any_address, 0, ADAPTER_TYPE_ANY)); + ipv6_any_address_network_ = std::make_unique( + "any", "any", ipv6_any_address, 0, ADAPTER_TYPE_ANY); ipv6_any_address_network_->set_default_local_address_provider(this); ipv6_any_address_network_->set_mdns_responder_provider(this); ipv6_any_address_network_->AddIP(ipv6_any_address); } - networks->push_back(ipv6_any_address_network_.get()); + networks.push_back(ipv6_any_address_network_.get()); + return networks; } -void NetworkManagerBase::GetNetworks(NetworkList* result) const { - result->clear(); - result->insert(result->begin(), networks_.begin(), networks_.end()); +std::vector NetworkManagerBase::GetNetworks() const { + std::vector result; + result.insert(result.begin(), networks_.begin(), networks_.end()); + return result; } -void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, - bool* changed) { +void NetworkManagerBase::MergeNetworkList( + std::vector> new_networks, + bool* changed) { NetworkManager::Stats stats; - MergeNetworkList(new_networks, changed, &stats); + MergeNetworkList(std::move(new_networks), changed, &stats); } -void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, - bool* changed, - NetworkManager::Stats* stats) { +void NetworkManagerBase::MergeNetworkList( + std::vector> new_networks, + bool* changed, + NetworkManager::Stats* stats) { *changed = false; // AddressList in this map will track IP addresses for all Networks // with the same key. std::map consolidated_address_list; - NetworkList list(new_networks); - absl::c_sort(list, CompareNetworks); + absl::c_sort(new_networks, rtc::webrtc_network_internal::CompareNetworks); // First, build a set of network-keys to the ipaddresses. - for (Network* network : list) { + for (auto& network : new_networks) { bool might_add_to_merged_list = false; std::string key = MakeNetworkKey(network->name(), network->prefix(), network->prefix_length()); + const std::vector& addresses = network->GetIPs(); if (consolidated_address_list.find(key) == consolidated_address_list.end()) { AddressList addrlist; - addrlist.net = network; - consolidated_address_list[key] = addrlist; + addrlist.net = std::move(network); + consolidated_address_list[key] = std::move(addrlist); might_add_to_merged_list = true; } - const std::vector& addresses = network->GetIPs(); AddressList& current_list = consolidated_address_list[key]; for (const InterfaceAddress& address : addresses) { current_list.ips.push_back(address); } - if (!might_add_to_merged_list) { - delete network; - } else { + if (might_add_to_merged_list) { if (current_list.ips[0].family() == AF_INET) { stats->ipv4_network_count++; } else { @@ -367,23 +370,24 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, // Next, look for existing network objects to re-use. // Result of Network merge. Element in this list should have unique key. - NetworkList merged_list; - for (const auto& kv : consolidated_address_list) { + std::vector merged_list; + for (auto& kv : consolidated_address_list) { const std::string& key = kv.first; - Network* net = kv.second.net; + std::unique_ptr net = std::move(kv.second.net); auto existing = networks_map_.find(key); if (existing == networks_map_.end()) { - // This network is new. Place it in the network map. - merged_list.push_back(net); - networks_map_[key] = net; + // This network is new. net->set_id(next_available_network_id_++); - // Also, we might have accumulated IPAddresses from the first + // We might have accumulated IPAddresses from the first // step, set it here. net->SetIPs(kv.second.ips, true); + // Place it in the network map. + merged_list.push_back(net.get()); + networks_map_[key] = std::move(net); *changed = true; } else { // This network exists in the map already. Reset its IP addresses. - Network* existing_net = existing->second; + Network* existing_net = existing->second.get(); *changed = existing_net->SetIPs(kv.second.ips, *changed); merged_list.push_back(existing_net); if (net->type() != ADAPTER_TYPE_UNKNOWN && @@ -405,9 +409,6 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, } } RTC_DCHECK(net->active()); - if (existing_net != net) { - delete net; - } } networks_map_[key]->set_mdns_responder_provider(this); } @@ -423,9 +424,9 @@ void NetworkManagerBase::MergeNetworkList(const NetworkList& new_networks, networks_ = merged_list; // Reset the active states of all networks. for (const auto& kv : networks_map_) { - Network* network = kv.second; + const std::unique_ptr& network = kv.second; // If `network` is in the newly generated `networks_`, it is active. - bool found = absl::c_linear_search(networks_, network); + bool found = absl::c_linear_search(networks_, network.get()); network->set_active(found); } absl::c_sort(networks_, SortNetworks); @@ -506,25 +507,17 @@ bool NetworkManagerBase::IsVpnMacAddress( return false; } -BasicNetworkManager::BasicNetworkManager() - : BasicNetworkManager(nullptr, nullptr) {} - -BasicNetworkManager::BasicNetworkManager(SocketFactory* socket_factory) - : BasicNetworkManager(nullptr, socket_factory) {} - -BasicNetworkManager::BasicNetworkManager( - NetworkMonitorFactory* network_monitor_factory) - : BasicNetworkManager(network_monitor_factory, nullptr) {} - BasicNetworkManager::BasicNetworkManager( NetworkMonitorFactory* network_monitor_factory, - SocketFactory* socket_factory) - : network_monitor_factory_(network_monitor_factory), + SocketFactory* socket_factory, + const webrtc::FieldTrialsView* field_trials) + : field_trials_(field_trials), + network_monitor_factory_(network_monitor_factory), socket_factory_(socket_factory), allow_mac_based_ipv6_( - webrtc::field_trial::IsEnabled("WebRTC-AllowMACBasedIPv6")), + field_trials_->IsEnabled("WebRTC-AllowMACBasedIPv6")), bind_using_ifname_( - !webrtc::field_trial::IsDisabled("WebRTC-BindUsingInterfaceName")) {} + !field_trials_->IsDisabled("WebRTC-BindUsingInterfaceName")) {} BasicNetworkManager::~BasicNetworkManager() { if (task_safety_flag_) { @@ -540,19 +533,21 @@ void BasicNetworkManager::OnNetworksChanged() { #if defined(__native_client__) -bool BasicNetworkManager::CreateNetworks(bool include_ignored, - NetworkList* networks) const { +bool BasicNetworkManager::CreateNetworks( + bool include_ignored, + std::vector>* networks) const { RTC_DCHECK_NOTREACHED(); RTC_LOG(LS_WARNING) << "BasicNetworkManager doesn't work on NaCl yet"; return false; } #elif defined(WEBRTC_POSIX) -void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, - IfAddrsConverter* ifaddrs_converter, - bool include_ignored, - NetworkList* networks) const { - NetworkMap current_networks; +void BasicNetworkManager::ConvertIfAddrs( + struct ifaddrs* interfaces, + IfAddrsConverter* ifaddrs_converter, + bool include_ignored, + std::vector>* networks) const { + std::map current_networks; for (struct ifaddrs* cursor = interfaces; cursor != nullptr; cursor = cursor->ifa_next) { @@ -626,9 +621,9 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, auto iter = current_networks.find(key); if (iter == current_networks.end()) { // TODO(phoglund): Need to recognize other types as well. - std::unique_ptr network( - new Network(cursor->ifa_name, cursor->ifa_name, prefix, prefix_length, - adapter_type)); + auto network = + std::make_unique(cursor->ifa_name, cursor->ifa_name, prefix, + prefix_length, adapter_type); network->set_default_local_address_provider(this); network->set_scope_id(scope_id); network->AddIP(ip); @@ -637,7 +632,7 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, network->set_network_preference(network_preference); if (include_ignored || !network->ignored()) { current_networks[key] = network.get(); - networks->push_back(network.release()); + networks->push_back(std::move(network)); } } else { Network* existing_network = iter->second; @@ -652,8 +647,9 @@ void BasicNetworkManager::ConvertIfAddrs(struct ifaddrs* interfaces, } } -bool BasicNetworkManager::CreateNetworks(bool include_ignored, - NetworkList* networks) const { +bool BasicNetworkManager::CreateNetworks( + bool include_ignored, + std::vector>* networks) const { struct ifaddrs* interfaces; int error = getifaddrs(&interfaces); if (error != 0) { @@ -714,9 +710,10 @@ unsigned int GetPrefix(PIP_ADAPTER_PREFIX prefixlist, return best_length; } -bool BasicNetworkManager::CreateNetworks(bool include_ignored, - NetworkList* networks) const { - NetworkMap current_networks; +bool BasicNetworkManager::CreateNetworks( + bool include_ignored, + std::vector>* networks) const { + std::map current_networks; // MSDN recommends a 15KB buffer for the first try at GetAdaptersAddresses. size_t buffer_size = 16384; std::unique_ptr adapter_info(new char[buffer_size]); @@ -739,17 +736,14 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, if (adapter_addrs->OperStatus == IfOperStatusUp) { PIP_ADAPTER_UNICAST_ADDRESS address = adapter_addrs->FirstUnicastAddress; PIP_ADAPTER_PREFIX prefixlist = adapter_addrs->FirstPrefix; - std::string name; - std::string description; -#if !defined(NDEBUG) - name = ToUtf8(adapter_addrs->FriendlyName, - wcslen(adapter_addrs->FriendlyName)); -#endif - description = ToUtf8(adapter_addrs->Description, - wcslen(adapter_addrs->Description)); + std::string description = ToUtf8(adapter_addrs->Description, + wcslen(adapter_addrs->Description)); + for (; address; address = address->Next) { -#if defined(NDEBUG) - name = rtc::ToString(count); + std::string name = rtc::ToString(count); +#if !defined(NDEBUG) + name = ToUtf8(adapter_addrs->FriendlyName, + wcslen(adapter_addrs->FriendlyName)); #endif IPAddress ip; @@ -824,8 +818,8 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, adapter_type = ADAPTER_TYPE_VPN; } - std::unique_ptr network(new Network( - name, description, prefix, prefix_length, adapter_type)); + auto network = std::make_unique(name, description, prefix, + prefix_length, adapter_type); network->set_underlying_type_for_vpn(vpn_underlying_adapter_type); network->set_default_local_address_provider(this); network->set_mdns_responder_provider(this); @@ -835,7 +829,7 @@ bool BasicNetworkManager::CreateNetworks(bool include_ignored, network->set_ignored(ignored); if (include_ignored || !network->ignored()) { current_networks[key] = network.get(); - networks->push_back(network.release()); + networks->push_back(std::move(network)); } } else { (*existing_network).second->AddIP(ip); @@ -934,7 +928,8 @@ void BasicNetworkManager::StartNetworkMonitor() { return; } if (!network_monitor_) { - network_monitor_.reset(network_monitor_factory_->CreateNetworkMonitor()); + network_monitor_.reset( + network_monitor_factory_->CreateNetworkMonitor(*field_trials_)); if (!network_monitor_) { return; } @@ -1003,13 +998,13 @@ void BasicNetworkManager::UpdateNetworksOnce() { if (!start_count_) return; - NetworkList list; + std::vector> list; if (!CreateNetworks(false, &list)) { SignalError(); } else { bool changed; NetworkManager::Stats stats; - MergeNetworkList(list, &changed, &stats); + MergeNetworkList(std::move(list), &changed, &stats); set_default_local_addresses(QueryDefaultLocalAddress(AF_INET), QueryDefaultLocalAddress(AF_INET6)); if (changed || !sent_first_update_) { @@ -1031,8 +1026,7 @@ void BasicNetworkManager::UpdateNetworksContinually() { void BasicNetworkManager::DumpNetworks() { RTC_DCHECK_RUN_ON(thread_); - NetworkList list; - GetNetworks(&list); + std::vector list = GetNetworks(); RTC_LOG(LS_INFO) << "NetworkManager detected " << list.size() << " networks:"; for (const Network* network : list) { RTC_LOG(LS_INFO) << network->ToString() << ": " << network->description() @@ -1055,26 +1049,8 @@ NetworkBindingResult BasicNetworkManager::BindSocketToNetwork( return network_monitor_->BindSocketToNetwork(socket_fd, address, if_name); } -Network::Network(const std::string& name, - const std::string& desc, - const IPAddress& prefix, - int prefix_length) - : name_(name), - description_(desc), - prefix_(prefix), - prefix_length_(prefix_length), - key_(MakeNetworkKey(name, prefix, prefix_length)), - scope_id_(0), - ignored_(false), - type_(ADAPTER_TYPE_UNKNOWN), - preference_(0), - use_differentiated_cellular_costs_(webrtc::field_trial::IsEnabled( - "WebRTC-UseDifferentiatedCellularCosts")), - add_network_cost_to_vpn_( - webrtc::field_trial::IsEnabled("WebRTC-AddNetworkCostToVpn")) {} - -Network::Network(const std::string& name, - const std::string& desc, +Network::Network(absl::string_view name, + absl::string_view desc, const IPAddress& prefix, int prefix_length, AdapterType type) @@ -1086,11 +1062,7 @@ Network::Network(const std::string& name, scope_id_(0), ignored_(false), type_(type), - preference_(0), - use_differentiated_cellular_costs_(webrtc::field_trial::IsEnabled( - "WebRTC-UseDifferentiatedCellularCosts")), - add_network_cost_to_vpn_( - webrtc::field_trial::IsEnabled("WebRTC-AddNetworkCostToVpn")) {} + preference_(0) {} Network::Network(const Network&) = default; @@ -1160,11 +1132,21 @@ webrtc::MdnsResponderInterface* Network::GetMdnsResponder() const { return mdns_responder_provider_->GetMdnsResponder(); } -uint16_t Network::GetCost() const { +uint16_t Network::GetCost(const webrtc::FieldTrialsView* field_trials) const { + return GetCost( + *webrtc::AlwaysValidPointer(field_trials)); +} + +uint16_t Network::GetCost(const webrtc::FieldTrialsView& field_trials) const { AdapterType type = IsVpn() ? underlying_type_for_vpn_ : type_; + const bool use_differentiated_cellular_costs = + field_trials.IsEnabled("WebRTC-UseDifferentiatedCellularCosts"); + const bool add_network_cost_to_vpn = + field_trials.IsEnabled("WebRTC-AddNetworkCostToVpn"); return ComputeNetworkCostByType(type, IsVpn(), - use_differentiated_cellular_costs_, - add_network_cost_to_vpn_); + use_differentiated_cellular_costs, + add_network_cost_to_vpn); } // This is the inverse of ComputeNetworkCostByType(). diff --git a/rtc_base/network.h b/rtc_base/network.h index 83a2f7d272..ef6c4f06be 100644 --- a/rtc_base/network.h +++ b/rtc_base/network.h @@ -19,10 +19,15 @@ #include #include +#include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "api/array_view.h" +#include "api/field_trials_view.h" #include "api/sequence_checker.h" +#include "api/transport/field_trial_based_config.h" #include "rtc_base/ip_address.h" #include "rtc_base/mdns_responder_interface.h" +#include "rtc_base/memory/always_valid_pointer.h" #include "rtc_base/network_monitor.h" #include "rtc_base/network_monitor_factory.h" #include "rtc_base/socket_factory.h" @@ -48,17 +53,22 @@ class Thread; // By default, ignore loopback interfaces on the host. const int kDefaultNetworkIgnoreMask = ADAPTER_TYPE_LOOPBACK; +namespace webrtc_network_internal { +bool CompareNetworks(const std::unique_ptr& a, + const std::unique_ptr& b); +} // namespace webrtc_network_internal + // Makes a string key for this network. Used in the network manager's maps. // Network objects are keyed on interface name, network prefix and the // length of that prefix. -std::string MakeNetworkKey(const std::string& name, +std::string MakeNetworkKey(absl::string_view name, const IPAddress& prefix, int prefix_length); // Utility function that attempts to determine an adapter type by an interface // name (e.g., "wlan0"). Can be used by NetworkManager subclasses when other // mechanisms fail to determine the type. -RTC_EXPORT AdapterType GetAdapterTypeFromName(const char* network_name); +RTC_EXPORT AdapterType GetAdapterTypeFromName(absl::string_view network_name); class DefaultLocalAddressProvider { public: @@ -112,8 +122,6 @@ class NetworkMask { class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, public MdnsResponderProvider { public: - typedef std::vector NetworkList; - // This enum indicates whether adapter enumeration is allowed. enum EnumerationPermission { ENUMERATION_ALLOWED, // Adapter enumeration is allowed. Getting 0 network @@ -123,9 +131,6 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, // GetAnyAddressNetworks() should be used instead. }; - NetworkManager(); - ~NetworkManager() override; - // Called when network list is updated. sigslot::signal0<> SignalNetworksChanged; @@ -148,7 +153,9 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, // It makes sure that repeated calls return the same object for a // given network, so that quality is tracked appropriately. Does not // include ignored networks. - virtual void GetNetworks(NetworkList* networks) const = 0; + // The returned vector of Network* is valid as long as the NetworkManager is + // alive. + virtual std::vector GetNetworks() const = 0; // Returns the current permission state of GetNetworks(). virtual EnumerationPermission enumeration_permission() const; @@ -160,9 +167,7 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, // // This method appends the "any address" networks to the list, such that this // can optionally be called after GetNetworks. - // - // TODO(guoweis): remove this body when chromium implements this. - virtual void GetAnyAddressNetworks(NetworkList* networks) {} + virtual std::vector GetAnyAddressNetworks() = 0; // Dumps the current list of networks in the network manager. virtual void DumpNetworks() {} @@ -186,11 +191,10 @@ class RTC_EXPORT NetworkManager : public DefaultLocalAddressProvider, // Base class for NetworkManager implementations. class RTC_EXPORT NetworkManagerBase : public NetworkManager { public: - NetworkManagerBase(); - ~NetworkManagerBase() override; + NetworkManagerBase(const webrtc::FieldTrialsView* field_trials = nullptr); - void GetNetworks(NetworkList* networks) const override; - void GetAnyAddressNetworks(NetworkList* networks) override; + std::vector GetNetworks() const override; + std::vector GetAnyAddressNetworks() override; EnumerationPermission enumeration_permission() const override; @@ -201,16 +205,16 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { static bool IsVpnMacAddress(rtc::ArrayView address); protected: - typedef std::map NetworkMap; // Updates `networks_` with the networks listed in `list`. If - // `network_map_` already has a Network object for a network listed + // `networks_map_` already has a Network object for a network listed // in the `list` then it is reused. Accept ownership of the Network // objects in the `list`. `changed` will be set to true if there is // any change in the network list. - void MergeNetworkList(const NetworkList& list, bool* changed); + void MergeNetworkList(std::vector> list, + bool* changed); // `stats` will be populated even if |*changed| is false. - void MergeNetworkList(const NetworkList& list, + void MergeNetworkList(std::vector> list, bool* changed, NetworkManager::Stats* stats); @@ -223,14 +227,18 @@ class RTC_EXPORT NetworkManagerBase : public NetworkManager { Network* GetNetworkFromAddress(const rtc::IPAddress& ip) const; + // To enable subclasses to get the networks list, without interfering with + // refactoring of the interface GetNetworks method. + const std::vector& GetNetworksInternal() const { return networks_; } + private: friend class NetworkTest; EnumerationPermission enumeration_permission_; - NetworkList networks_; + std::vector networks_; - NetworkMap networks_map_; + std::map> networks_map_; std::unique_ptr ipv4_any_address_network_; std::unique_ptr ipv6_any_address_network_; @@ -254,15 +262,26 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, public NetworkBinderInterface, public sigslot::has_slots<> { public: + // This version is used by chromium. ABSL_DEPRECATED( "Use the version with socket_factory, see bugs.webrtc.org/13145") - BasicNetworkManager(); - explicit BasicNetworkManager(SocketFactory* socket_factory); - ABSL_DEPRECATED( - "Use the version with socket_factory, see bugs.webrtc.org/13145") - explicit BasicNetworkManager(NetworkMonitorFactory* network_monitor_factory); + explicit BasicNetworkManager( + const webrtc::FieldTrialsView* field_trials = nullptr) + : BasicNetworkManager( + /* network_monitor_factory= */ nullptr, + /* socket_factory= */ nullptr, + field_trials) {} + + // This is used by lots of downstream code. + BasicNetworkManager(SocketFactory* socket_factory, + const webrtc::FieldTrialsView* field_trials = nullptr) + : BasicNetworkManager(/* network_monitor_factory= */ nullptr, + socket_factory, + field_trials) {} + BasicNetworkManager(NetworkMonitorFactory* network_monitor_factory, - SocketFactory* socket_factory); + SocketFactory* socket_factory, + const webrtc::FieldTrialsView* field_trials = nullptr); ~BasicNetworkManager() override; void StartUpdating() override; @@ -301,11 +320,13 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, void ConvertIfAddrs(ifaddrs* interfaces, IfAddrsConverter* converter, bool include_ignored, - NetworkList* networks) const RTC_RUN_ON(thread_); + std::vector>* networks) const + RTC_RUN_ON(thread_); #endif // defined(WEBRTC_POSIX) // Creates a network object for each network available on the machine. - bool CreateNetworks(bool include_ignored, NetworkList* networks) const + bool CreateNetworks(bool include_ignored, + std::vector>* networks) const RTC_RUN_ON(thread_); // Determines if a network should be ignored. This should only be determined @@ -335,6 +356,10 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, Thread* thread_ = nullptr; bool sent_first_update_ = true; int start_count_ = 0; + // Chromium create BasicNetworkManager() w/o field trials. + webrtc::AlwaysValidPointer + field_trials_; std::vector network_ignore_list_; NetworkMonitorFactory* const network_monitor_factory_; SocketFactory* const socket_factory_; @@ -350,26 +375,34 @@ class RTC_EXPORT BasicNetworkManager : public NetworkManagerBase, // Represents a Unix-type network interface, with a name and single address. class RTC_EXPORT Network { public: - Network(const std::string& name, - const std::string& description, + Network(absl::string_view name, + absl::string_view description, const IPAddress& prefix, - int prefix_length); - - Network(const std::string& name, - const std::string& description, + int prefix_length) + : Network(name, + description, + prefix, + prefix_length, + rtc::ADAPTER_TYPE_UNKNOWN) {} + + Network(absl::string_view name, + absl::string_view description, const IPAddress& prefix, int prefix_length, AdapterType type); + Network(const Network&); ~Network(); // This signal is fired whenever type() or underlying_type_for_vpn() changes. - sigslot::signal1 SignalTypeChanged; + // Mutable, to support connecting on the const Network passed to cricket::Port + // constructor. + mutable sigslot::signal1 SignalTypeChanged; // This signal is fired whenever network preference changes. sigslot::signal1 SignalNetworkPreferenceChanged; - const DefaultLocalAddressProvider* default_local_address_provider() { + const DefaultLocalAddressProvider* default_local_address_provider() const { return default_local_address_provider_; } void set_default_local_address_provider( @@ -494,7 +527,15 @@ class RTC_EXPORT Network { } } - uint16_t GetCost() const; + // Note: This function is called "rarely". + // Twice per Network in BasicPortAllocator if + // PORTALLOCATOR_DISABLE_COSTLY_NETWORKS. Once in Port::Construct() (and when + // Port::OnNetworkTypeChanged is called). + ABSL_DEPRECATED( + "Use the version with field trials, see bugs.webrtc.org/webrtc:10335") + uint16_t GetCost(const webrtc::FieldTrialsView* field_trials = nullptr) const; + uint16_t GetCost(const webrtc::FieldTrialsView& field_trials) const; + // A unique id assigned by the network manager, which may be signaled // to the remote side in the candidate. uint16_t id() const { return id_; } @@ -546,8 +587,6 @@ class RTC_EXPORT Network { int preference_; bool active_ = true; uint16_t id_ = 0; - bool use_differentiated_cellular_costs_ = false; - bool add_network_cost_to_vpn_ = false; NetworkPreference network_preference_ = NetworkPreference::NEUTRAL; friend class NetworkManager; diff --git a/rtc_base/network_monitor.h b/rtc_base/network_monitor.h index c0eea1ff52..72571d1497 100644 --- a/rtc_base/network_monitor.h +++ b/rtc_base/network_monitor.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/network_constants.h" namespace rtc { @@ -78,12 +79,12 @@ class NetworkMonitorInterface { virtual void Start() = 0; virtual void Stop() = 0; - virtual AdapterType GetAdapterType(const std::string& interface_name) = 0; + virtual AdapterType GetAdapterType(absl::string_view interface_name) = 0; virtual AdapterType GetVpnUnderlyingAdapterType( - const std::string& interface_name) = 0; + absl::string_view interface_name) = 0; virtual NetworkPreference GetNetworkPreference( - const std::string& interface_name) = 0; + absl::string_view interface_name) = 0; // Does `this` NetworkMonitorInterface implement BindSocketToNetwork? // Only Android returns true. @@ -94,7 +95,7 @@ class NetworkMonitorInterface { virtual NetworkBindingResult BindSocketToNetwork( int socket_fd, const IPAddress& address, - const std::string& interface_name) { + absl::string_view interface_name) { return NetworkBindingResult::NOT_IMPLEMENTED; } @@ -107,7 +108,7 @@ class NetworkMonitorInterface { // These specific use case this was added for was a phone with two SIM cards, // where attempting to use all interfaces returned from getifaddrs caused the // connection to be dropped. - virtual bool IsAdapterAvailable(const std::string& interface_name) { + virtual bool IsAdapterAvailable(absl::string_view interface_name) { return true; } diff --git a/rtc_base/network_monitor_factory.h b/rtc_base/network_monitor_factory.h index dadcd4aa8a..c76ed97d8c 100644 --- a/rtc_base/network_monitor_factory.h +++ b/rtc_base/network_monitor_factory.h @@ -11,6 +11,10 @@ #ifndef RTC_BASE_NETWORK_MONITOR_FACTORY_H_ #define RTC_BASE_NETWORK_MONITOR_FACTORY_H_ +namespace webrtc { +class FieldTrialsView; +} // namespace webrtc + namespace rtc { // Forward declaring this so it's not part of the API surface; it's only @@ -24,7 +28,8 @@ class NetworkMonitorInterface; */ class NetworkMonitorFactory { public: - virtual NetworkMonitorInterface* CreateNetworkMonitor() = 0; + virtual NetworkMonitorInterface* CreateNetworkMonitor( + const webrtc::FieldTrialsView& field_trials) = 0; virtual ~NetworkMonitorFactory(); diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc index 5635f5d868..bfb30b2c93 100644 --- a/rtc_base/network_unittest.cc +++ b/rtc_base/network_unittest.cc @@ -18,6 +18,7 @@ #include "absl/algorithm/container.h" #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/net_helpers.h" #include "rtc_base/network_monitor.h" @@ -35,6 +36,7 @@ #include "rtc_base/logging.h" // For RTC_LOG_GLE #endif #include "test/field_trial.h" +#include "test/scoped_key_value_config.h" using ::testing::Contains; using ::testing::Not; @@ -45,7 +47,7 @@ namespace rtc { namespace { -IPAddress IPFromString(const std::string& str) { +IPAddress IPFromString(absl::string_view str) { IPAddress ip; RTC_CHECK(IPFromString(str, &ip)); return ip; @@ -56,7 +58,7 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { void Start() override { started_ = true; } void Stop() override { started_ = false; } bool started() { return started_; } - AdapterType GetAdapterType(const std::string& if_name) override { + AdapterType GetAdapterType(absl::string_view if_name) override { // Note that the name matching rules are different from the // GetAdapterTypeFromName in NetworkManager. if (absl::StartsWith(if_name, "wifi")) { @@ -67,14 +69,14 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { } return ADAPTER_TYPE_UNKNOWN; } - AdapterType GetVpnUnderlyingAdapterType(const std::string& if_name) override { + AdapterType GetVpnUnderlyingAdapterType(absl::string_view if_name) override { return ADAPTER_TYPE_UNKNOWN; } - NetworkPreference GetNetworkPreference(const std::string& if_name) override { + NetworkPreference GetNetworkPreference(absl::string_view if_name) override { return NetworkPreference::NEUTRAL; } - bool IsAdapterAvailable(const std::string& if_name) override { + bool IsAdapterAvailable(absl::string_view if_name) override { return absl::c_count(unavailable_adapters_, if_name) == 0; } @@ -85,16 +87,15 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { bool SupportsBindSocketToNetwork() const override { return true; } - NetworkBindingResult BindSocketToNetwork( - int socket_fd, - const IPAddress& address, - const std::string& if_name) override { + NetworkBindingResult BindSocketToNetwork(int socket_fd, + const IPAddress& address, + absl::string_view 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) { + if (if_name.find(iter) != absl::string_view::npos) { return NetworkBindingResult::SUCCESS; } } @@ -121,7 +122,8 @@ class FakeNetworkMonitor : public NetworkMonitorInterface { class FakeNetworkMonitorFactory : public NetworkMonitorFactory { public: FakeNetworkMonitorFactory() {} - NetworkMonitorInterface* CreateNetworkMonitor() override { + NetworkMonitorInterface* CreateNetworkMonitor( + const webrtc::FieldTrialsView& field_trials) override { return new FakeNetworkMonitor(); } }; @@ -138,6 +140,16 @@ bool SameNameAndPrefix(const rtc::Network& a, const rtc::Network& b) { return true; } +std::vector CopyNetworkPointers( + const std::vector>& owning_list) { + std::vector ptr_list; + ptr_list.reserve(owning_list.size()); + for (const auto& network : owning_list) { + ptr_list.push_back(network.get()); + } + return ptr_list; +} + } // namespace class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { @@ -148,10 +160,10 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { NetworkManager::Stats MergeNetworkList( BasicNetworkManager& network_manager, - const NetworkManager::NetworkList& list, + std::vector> list, bool* changed) { NetworkManager::Stats stats; - network_manager.MergeNetworkList(list, changed, &stats); + network_manager.MergeNetworkList(std::move(list), changed, &stats); return stats; } @@ -167,11 +179,11 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { return network_manager.QueryDefaultLocalAddress(family); } - NetworkManager::NetworkList GetNetworks( + std::vector> GetNetworks( const BasicNetworkManager& network_manager, bool include_ignored) { RTC_DCHECK_RUN_ON(network_manager.thread_); - NetworkManager::NetworkList list; + std::vector> list; network_manager.CreateNetworks(include_ignored, &list); return list; } @@ -182,26 +194,23 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { network_manager.network_monitor_.get()); } void ClearNetworks(BasicNetworkManager& network_manager) { - for (const auto& kv : network_manager.networks_map_) { - delete kv.second; - } network_manager.networks_.clear(); network_manager.networks_map_.clear(); } AdapterType GetAdapterType(BasicNetworkManager& network_manager) { - BasicNetworkManager::NetworkList list; - network_manager.GetNetworks(&list); + std::vector list = network_manager.GetNetworks(); RTC_CHECK_EQ(1, list.size()); return list[0]->type(); } #if defined(WEBRTC_POSIX) // Separated from CreateNetworks for tests. - static void CallConvertIfAddrs(const BasicNetworkManager& network_manager, - struct ifaddrs* interfaces, - bool include_ignored, - NetworkManager::NetworkList* networks) { + static void CallConvertIfAddrs( + const BasicNetworkManager& network_manager, + struct ifaddrs* interfaces, + bool include_ignored, + std::vector>* networks) { RTC_DCHECK_RUN_ON(network_manager.thread_); // Use the base IfAddrsConverter for test cases. std::unique_ptr ifaddrs_converter(new IfAddrsConverter()); @@ -209,7 +218,7 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { include_ignored, networks); } - struct sockaddr_in6* CreateIpv6Addr(const std::string& ip_string, + struct sockaddr_in6* CreateIpv6Addr(absl::string_view ip_string, uint32_t scope_id) { struct sockaddr_in6* ipv6_addr = static_cast(malloc(sizeof(struct sockaddr_in6))); @@ -225,8 +234,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { // Pointers created here need to be released via ReleaseIfAddrs. struct ifaddrs* AddIpv6Address(struct ifaddrs* list, char* if_name, - const std::string& ipv6_address, - const std::string& ipv6_netmask, + absl::string_view ipv6_address, + absl::string_view ipv6_netmask, uint32_t scope_id) { struct ifaddrs* if_addr = new struct ifaddrs; memset(if_addr, 0, sizeof(struct ifaddrs)); @@ -241,20 +250,20 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { } struct ifaddrs* InstallIpv6Network(char* if_name, - const std::string& ipv6_address, - const std::string& ipv6_mask, + absl::string_view ipv6_address, + absl::string_view ipv6_mask, BasicNetworkManager& network_manager) { ifaddrs* addr_list = nullptr; addr_list = AddIpv6Address(addr_list, if_name, ipv6_address, ipv6_mask, 0); - NetworkManager::NetworkList result; + std::vector> result; bool changed; NetworkManager::Stats stats; CallConvertIfAddrs(network_manager, addr_list, true, &result); - network_manager.MergeNetworkList(result, &changed, &stats); + network_manager.MergeNetworkList(std::move(result), &changed, &stats); return addr_list; } - struct sockaddr_in* CreateIpv4Addr(const std::string& ip_string) { + struct sockaddr_in* CreateIpv4Addr(absl::string_view ip_string) { struct sockaddr_in* ipv4_addr = static_cast(malloc(sizeof(struct sockaddr_in))); memset(ipv4_addr, 0, sizeof(struct sockaddr_in)); @@ -268,8 +277,8 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { // Pointers created here need to be released via ReleaseIfAddrs. struct ifaddrs* AddIpv4Address(struct ifaddrs* list, char* if_name, - const std::string& ipv4_address, - const std::string& ipv4_netmask) { + absl::string_view ipv4_address, + absl::string_view ipv4_netmask) { struct ifaddrs* if_addr = new struct ifaddrs; memset(if_addr, 0, sizeof(struct ifaddrs)); if_addr->ifa_name = if_name; @@ -283,16 +292,16 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { } struct ifaddrs* InstallIpv4Network(char* if_name, - const std::string& ipv4_address, - const std::string& ipv4_mask, + absl::string_view ipv4_address, + absl::string_view ipv4_mask, BasicNetworkManager& network_manager) { ifaddrs* addr_list = nullptr; addr_list = AddIpv4Address(addr_list, if_name, ipv4_address, ipv4_mask); - NetworkManager::NetworkList result; + std::vector> result; bool changed; NetworkManager::Stats stats; CallConvertIfAddrs(network_manager, addr_list, true, &result); - network_manager.MergeNetworkList(result, &changed, &stats); + network_manager.MergeNetworkList(std::move(result), &changed, &stats); return addr_list; } @@ -309,14 +318,18 @@ class NetworkTest : public ::testing::Test, public sigslot::has_slots<> { #endif // defined(WEBRTC_POSIX) protected: + webrtc::test::ScopedKeyValueConfig field_trials_; bool callback_called_; }; class TestBasicNetworkManager : public BasicNetworkManager { public: TestBasicNetworkManager(NetworkMonitorFactory* network_monitor_factory, - SocketFactory* socket_factory) - : BasicNetworkManager(network_monitor_factory, socket_factory) {} + SocketFactory* socket_factory, + const webrtc::FieldTrialsView& field_trials) + : BasicNetworkManager(network_monitor_factory, + socket_factory, + &field_trials) {} using BasicNetworkManager::QueryDefaultLocalAddress; using BasicNetworkManager::set_default_local_addresses; }; @@ -369,10 +382,9 @@ TEST_F(NetworkTest, TestIgnoreList) { TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); - NetworkManager::NetworkList result = GetNetworks(manager, true); + std::vector> result = GetNetworks(manager, true); // We should be able to bind to any addresses we find. - NetworkManager::NetworkList::iterator it; - for (it = result.begin(); it != result.end(); ++it) { + for (auto it = result.begin(); it != result.end(); ++it) { sockaddr_storage storage; memset(&storage, 0, sizeof(storage)); IPAddress ip = (*it)->GetBestIP(); @@ -396,7 +408,6 @@ TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { close(fd); #endif } - delete (*it); } } @@ -404,7 +415,7 @@ TEST_F(NetworkTest, DISABLED_TestCreateNetworks) { // ALLOWED. TEST_F(NetworkTest, TestUpdateNetworks) { PhysicalSocketServer socket_server; - BasicNetworkManager manager(nullptr, &socket_server); + BasicNetworkManager manager(nullptr, &socket_server, &field_trials_); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); EXPECT_EQ(NetworkManager::ENUMERATION_ALLOWED, @@ -447,81 +458,78 @@ TEST_F(NetworkTest, TestBasicMergeNetworkList) { BasicNetworkManager manager(&socket_server); // Add ipv4_network1 to the list of networks. - NetworkManager::NetworkList list; - list.push_back(new Network(ipv4_network1)); + std::vector> list; + list.push_back(std::make_unique(ipv4_network1)); bool changed; - NetworkManager::Stats stats = MergeNetworkList(manager, list, &changed); + NetworkManager::Stats stats = + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); EXPECT_EQ(stats.ipv6_network_count, 0); EXPECT_EQ(stats.ipv4_network_count, 1); - list.clear(); + list.clear(); // It is fine to call .clear() on a moved-from vector. - manager.GetNetworks(&list); - EXPECT_EQ(1U, list.size()); - EXPECT_TRUE(SameNameAndPrefix(ipv4_network1, *list[0])); - Network* net1 = list[0]; + std::vector current = manager.GetNetworks(); + EXPECT_EQ(1U, current.size()); + EXPECT_TRUE(SameNameAndPrefix(ipv4_network1, *current[0])); + const Network* net1 = current[0]; uint16_t net_id1 = net1->id(); EXPECT_EQ(1, net_id1); - list.clear(); // Replace ipv4_network1 with ipv4_network2. - list.push_back(new Network(ipv4_network2)); - stats = MergeNetworkList(manager, list, &changed); + list.push_back(std::make_unique(ipv4_network2)); + stats = MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); EXPECT_EQ(stats.ipv6_network_count, 0); EXPECT_EQ(stats.ipv4_network_count, 1); list.clear(); - manager.GetNetworks(&list); - EXPECT_EQ(1U, list.size()); - EXPECT_TRUE(SameNameAndPrefix(ipv4_network2, *list[0])); - Network* net2 = list[0]; + current = manager.GetNetworks(); + EXPECT_EQ(1U, current.size()); + EXPECT_TRUE(SameNameAndPrefix(ipv4_network2, *current[0])); + const Network* net2 = current[0]; uint16_t net_id2 = net2->id(); // Network id will increase. EXPECT_LT(net_id1, net_id2); - list.clear(); // Add Network2 back. - list.push_back(new Network(ipv4_network1)); - list.push_back(new Network(ipv4_network2)); - stats = MergeNetworkList(manager, list, &changed); + list.push_back(std::make_unique(ipv4_network1)); + list.push_back(std::make_unique(ipv4_network2)); + stats = MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); EXPECT_EQ(stats.ipv6_network_count, 0); EXPECT_EQ(stats.ipv4_network_count, 2); list.clear(); // Verify that we get previous instances of Network objects. - manager.GetNetworks(&list); - EXPECT_EQ(2U, list.size()); - EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || - (net1 == list[1] && net2 == list[0])); - EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) || - (net_id1 == list[1]->id() && net_id2 == list[0]->id())); - list.clear(); + current = manager.GetNetworks(); + EXPECT_EQ(2U, current.size()); + EXPECT_TRUE((net1 == current[0] && net2 == current[1]) || + (net1 == current[1] && net2 == current[0])); + EXPECT_TRUE((net_id1 == current[0]->id() && net_id2 == current[1]->id()) || + (net_id1 == current[1]->id() && net_id2 == current[0]->id())); // Call MergeNetworkList() again and verify that we don't get update // notification. - list.push_back(new Network(ipv4_network2)); - list.push_back(new Network(ipv4_network1)); - stats = MergeNetworkList(manager, list, &changed); + list.push_back(std::make_unique(ipv4_network2)); + list.push_back(std::make_unique(ipv4_network1)); + stats = MergeNetworkList(manager, std::move(list), &changed); EXPECT_FALSE(changed); EXPECT_EQ(stats.ipv6_network_count, 0); EXPECT_EQ(stats.ipv4_network_count, 2); list.clear(); // Verify that we get previous instances of Network objects. - manager.GetNetworks(&list); - EXPECT_EQ(2U, list.size()); - EXPECT_TRUE((net1 == list[0] && net2 == list[1]) || - (net1 == list[1] && net2 == list[0])); - EXPECT_TRUE((net_id1 == list[0]->id() && net_id2 == list[1]->id()) || - (net_id1 == list[1]->id() && net_id2 == list[0]->id())); - list.clear(); + current = manager.GetNetworks(); + EXPECT_EQ(2U, current.size()); + EXPECT_TRUE((net1 == current[0] && net2 == current[1]) || + (net1 == current[1] && net2 == current[0])); + EXPECT_TRUE((net_id1 == current[0]->id() && net_id2 == current[1]->id()) || + (net_id1 == current[1]->id() && net_id2 == current[0]->id())); } // Sets up some test IPv6 networks and appends them to list. // Four networks are added - public and link local, for two interfaces. -void SetupNetworks(NetworkManager::NetworkList* list) { +void SetupNetworks(std::vector>* list) { IPAddress ip; IPAddress prefix; EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:ef12", &ip)); @@ -545,10 +553,10 @@ void SetupNetworks(NetworkManager::NetworkList* list) { Network ipv6_eth1_publicnetwork1_ip1("test_eth1", "Test NetworkAdapter 1", prefix, 64); ipv6_eth1_publicnetwork1_ip1.AddIP(ip); - list->push_back(new Network(ipv6_eth0_linklocalnetwork)); - list->push_back(new Network(ipv6_eth1_linklocalnetwork)); - list->push_back(new Network(ipv6_eth0_publicnetwork1_ip1)); - list->push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); + list->push_back(std::make_unique(ipv6_eth0_linklocalnetwork)); + list->push_back(std::make_unique(ipv6_eth1_linklocalnetwork)); + list->push_back(std::make_unique(ipv6_eth0_publicnetwork1_ip1)); + list->push_back(std::make_unique(ipv6_eth1_publicnetwork1_ip1)); } // Test that the basic network merging case works. @@ -557,16 +565,16 @@ TEST_F(NetworkTest, TestIPv6MergeNetworkList) { BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); - NetworkManager::NetworkList original_list; - SetupNetworks(&original_list); + std::vector> networks; + SetupNetworks(&networks); + std::vector original_list = CopyNetworkPointers(networks); bool changed = false; NetworkManager::Stats stats = - MergeNetworkList(manager, original_list, &changed); + MergeNetworkList(manager, std::move(networks), &changed); EXPECT_TRUE(changed); EXPECT_EQ(stats.ipv6_network_count, 4); EXPECT_EQ(stats.ipv4_network_count, 0); - NetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); // Verify that the original members are in the merged list. EXPECT_THAT(list, UnorderedElementsAreArray(original_list)); } @@ -579,19 +587,21 @@ TEST_F(NetworkTest, TestNoChangeMerge) { BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); - NetworkManager::NetworkList original_list; - SetupNetworks(&original_list); + std::vector> networks; + SetupNetworks(&networks); + std::vector original_list = CopyNetworkPointers(networks); bool changed = false; - MergeNetworkList(manager, original_list, &changed); + MergeNetworkList(manager, std::move(networks), &changed); EXPECT_TRUE(changed); // Second list that describes the same networks but with new objects. - NetworkManager::NetworkList second_list; - SetupNetworks(&second_list); + std::vector> second_networks; + SetupNetworks(&second_networks); + std::vector second_list = + CopyNetworkPointers(second_networks); changed = false; - MergeNetworkList(manager, second_list, &changed); + MergeNetworkList(manager, std::move(second_networks), &changed); EXPECT_FALSE(changed); - NetworkManager::NetworkList resulting_list; - manager.GetNetworks(&resulting_list); + std::vector resulting_list = manager.GetNetworks(); // Verify that the original members are in the merged list. EXPECT_THAT(resulting_list, UnorderedElementsAreArray(original_list)); // Doublecheck that the new networks aren't in the list. @@ -608,47 +618,48 @@ TEST_F(NetworkTest, MergeWithChangedIP) { BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); - NetworkManager::NetworkList original_list; + std::vector> original_list; SetupNetworks(&original_list); // Make a network that we're going to change. IPAddress ip; EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:faa:fee:faa", &ip)); IPAddress prefix = TruncateIP(ip, 64); - Network* network_to_change = - new Network("test_eth0", "Test Network Adapter 1", prefix, 64); - Network* changed_network = new Network(*network_to_change); + std::unique_ptr network_to_change = std::make_unique( + "test_eth0", "Test Network Adapter 1", prefix, 64); + std::unique_ptr changed_network = + std::make_unique(*network_to_change); network_to_change->AddIP(ip); IPAddress changed_ip; EXPECT_TRUE(IPFromString("2401:fa01:4:1000:be30:f00:f00:f00", &changed_ip)); changed_network->AddIP(changed_ip); - original_list.push_back(network_to_change); + const Network* const network_to_change_ptr = network_to_change.get(); + original_list.push_back(std::move(network_to_change)); + const size_t original_size = original_list.size(); bool changed = false; - MergeNetworkList(manager, original_list, &changed); - NetworkManager::NetworkList second_list; + MergeNetworkList(manager, std::move(original_list), &changed); + std::vector> second_list; SetupNetworks(&second_list); - second_list.push_back(changed_network); + second_list.push_back(std::move(changed_network)); changed = false; - MergeNetworkList(manager, second_list, &changed); + MergeNetworkList(manager, std::move(second_list), &changed); EXPECT_TRUE(changed); - NetworkManager::NetworkList list; - manager.GetNetworks(&list); - EXPECT_EQ(original_list.size(), list.size()); + std::vector list = manager.GetNetworks(); + EXPECT_EQ(original_size, list.size()); // Make sure the original network is still in the merged list. - EXPECT_THAT(list, Contains(network_to_change)); - EXPECT_EQ(changed_ip, network_to_change->GetIPs().at(0)); + EXPECT_THAT(list, Contains(network_to_change_ptr)); + EXPECT_EQ(changed_ip, network_to_change_ptr->GetIPs().at(0)); } -// Testing a similar case to above, but checking that a network can be updated -// with additional IPs (not just a replacement). TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) { PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); - NetworkManager::NetworkList original_list; + std::vector> original_list; SetupNetworks(&original_list); + const Network* const network_ptr = original_list[2].get(); bool changed = false; - MergeNetworkList(manager, original_list, &changed); + MergeNetworkList(manager, std::move(original_list), &changed); EXPECT_TRUE(changed); IPAddress ip; IPAddress check_ip; @@ -661,30 +672,33 @@ TEST_F(NetworkTest, TestMultipleIPMergeNetworkList) { // This is the IP that already existed in the public network on eth0. EXPECT_TRUE(IPFromString("2401:fa00:4:1000:be30:5bff:fee5:c3", &check_ip)); ipv6_eth0_publicnetwork1_ip2.AddIP(ip); - original_list.push_back(new Network(ipv6_eth0_publicnetwork1_ip2)); + + std::vector> second_list; + SetupNetworks(&second_list); + second_list.push_back( + std::make_unique(ipv6_eth0_publicnetwork1_ip2)); changed = false; - MergeNetworkList(manager, original_list, &changed); + const auto network_copy = std::make_unique(*second_list[2]); + MergeNetworkList(manager, std::move(second_list), &changed); EXPECT_TRUE(changed); // There should still be four networks. - NetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); EXPECT_EQ(4U, list.size()); // Check the gathered IPs. int matchcount = 0; - for (NetworkManager::NetworkList::iterator it = list.begin(); - it != list.end(); ++it) { - if (SameNameAndPrefix(**it, *original_list[2])) { + for (const Network* network : list) { + if (SameNameAndPrefix(*network, *network_copy)) { ++matchcount; EXPECT_EQ(1, matchcount); // This should be the same network object as before. - EXPECT_EQ((*it), original_list[2]); + EXPECT_EQ(network, network_ptr); // But with two addresses now. - EXPECT_THAT((*it)->GetIPs(), + EXPECT_THAT(network->GetIPs(), UnorderedElementsAre(InterfaceAddress(check_ip), InterfaceAddress(ip))); } else { // Check the IP didn't get added anywhere it wasn't supposed to. - EXPECT_THAT((*it)->GetIPs(), Not(Contains(InterfaceAddress(ip)))); + EXPECT_THAT(network->GetIPs(), Not(Contains(InterfaceAddress(ip)))); } } } @@ -695,10 +709,10 @@ TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) { BasicNetworkManager manager(&socket_server); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); - NetworkManager::NetworkList original_list; + std::vector> original_list; SetupNetworks(&original_list); bool changed = false; - MergeNetworkList(manager, original_list, &changed); + MergeNetworkList(manager, std::move(original_list), &changed); EXPECT_TRUE(changed); IPAddress ip; IPAddress prefix; @@ -708,25 +722,26 @@ TEST_F(NetworkTest, TestMultiplePublicNetworksOnOneInterfaceMerge) { Network ipv6_eth0_publicnetwork2_ip1("test_eth0", "Test NetworkAdapter 1", prefix, 64); ipv6_eth0_publicnetwork2_ip1.AddIP(ip); - original_list.push_back(new Network(ipv6_eth0_publicnetwork2_ip1)); + std::vector> second_list; + SetupNetworks(&second_list); + second_list.push_back( + std::make_unique(ipv6_eth0_publicnetwork2_ip1)); changed = false; - MergeNetworkList(manager, original_list, &changed); + MergeNetworkList(manager, std::move(second_list), &changed); EXPECT_TRUE(changed); // There should be five networks now. - NetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); EXPECT_EQ(5U, list.size()); // Check the resulting addresses. - for (NetworkManager::NetworkList::iterator it = list.begin(); - it != list.end(); ++it) { - if ((*it)->prefix() == ipv6_eth0_publicnetwork2_ip1.prefix() && - (*it)->name() == ipv6_eth0_publicnetwork2_ip1.name()) { + for (const Network* network : list) { + if (network->prefix() == ipv6_eth0_publicnetwork2_ip1.prefix() && + network->name() == ipv6_eth0_publicnetwork2_ip1.name()) { // Check the new network has 1 IP and that it's the correct one. - EXPECT_EQ(1U, (*it)->GetIPs().size()); - EXPECT_EQ(ip, (*it)->GetIPs().at(0)); + EXPECT_EQ(1U, network->GetIPs().size()); + EXPECT_EQ(ip, network->GetIPs().at(0)); } else { // Check the IP didn't get added anywhere it wasn't supposed to. - EXPECT_THAT((*it)->GetIPs(), Not(Contains(InterfaceAddress(ip)))); + EXPECT_THAT(network->GetIPs(), Not(Contains(InterfaceAddress(ip)))); } } } @@ -736,9 +751,9 @@ TEST_F(NetworkTest, TestCreateAndDumpNetworks) { PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); manager.StartUpdating(); - NetworkManager::NetworkList list = GetNetworks(manager, true); + std::vector> list = GetNetworks(manager, true); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); manager.DumpNetworks(); } @@ -747,20 +762,13 @@ TEST_F(NetworkTest, TestIPv6Toggle) { BasicNetworkManager manager(&socket_server); manager.StartUpdating(); bool ipv6_found = false; - NetworkManager::NetworkList list; - list = GetNetworks(manager, true); - for (NetworkManager::NetworkList::iterator it = list.begin(); - it != list.end(); ++it) { - if ((*it)->prefix().family() == AF_INET6) { + for (const auto& network : GetNetworks(manager, true)) { + if (network->prefix().family() == AF_INET6) { ipv6_found = true; break; } } EXPECT_TRUE(ipv6_found); - for (NetworkManager::NetworkList::iterator it = list.begin(); - it != list.end(); ++it) { - delete (*it); - } } // Test that when network interfaces are sorted and given preference values, @@ -780,14 +788,14 @@ TEST_F(NetworkTest, IPv6NetworksPreferredOverIPv4) { prefix, 64); ipv6_eth1_publicnetwork1_ip1.AddIP(ip); - NetworkManager::NetworkList list; - list.push_back(new Network(ipv4_network1)); - list.push_back(new Network(ipv6_eth1_publicnetwork1_ip1)); - Network* net1 = list[0]; - Network* net2 = list[1]; + std::vector> list; + list.push_back(std::make_unique(ipv4_network1)); + list.push_back(std::make_unique(ipv6_eth1_publicnetwork1_ip1)); + const Network* net1 = list[0].get(); + const Network* net2 = list[1].get(); bool changed = false; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); ASSERT_TRUE(changed); // After sorting IPv6 network should be higher order than IPv4 networks. EXPECT_TRUE(net1->preference() < net2->preference()); @@ -797,24 +805,26 @@ TEST_F(NetworkTest, IPv6NetworksPreferredOverIPv4) { // to be preference-ordered by name. For example, "eth0" before "eth1". TEST_F(NetworkTest, NetworksSortedByInterfaceName) { PhysicalSocketServer socket_server; - BasicNetworkManager manager(&socket_server); - Network* eth0 = new Network("test_eth0", "Test Network Adapter 1", - IPAddress(0x65432100U), 24); + BasicNetworkManager manager(&socket_server, &field_trials_); + auto eth0 = std::make_unique("test_eth0", "Test Network Adapter 1", + IPAddress(0x65432100U), 24); eth0->AddIP(IPAddress(0x65432100U)); - Network* eth1 = new Network("test_eth1", "Test Network Adapter 2", - IPAddress(0x12345600U), 24); + auto eth1 = std::make_unique("test_eth1", "Test Network Adapter 2", + IPAddress(0x12345600U), 24); eth1->AddIP(IPAddress(0x12345600U)); - NetworkManager::NetworkList list; + std::vector> list; + const Network* eth0_ptr = eth0.get(); + const Network* eth1_ptr = eth1.get(); // Add them to the list in the opposite of the expected sorted order, to // ensure sorting actually occurs. - list.push_back(eth1); - list.push_back(eth0); + list.push_back(std::move(eth1)); + list.push_back(std::move(eth0)); bool changed = false; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); ASSERT_TRUE(changed); // "test_eth0" should be preferred over "test_eth1". - EXPECT_TRUE(eth0->preference() > eth1->preference()); + EXPECT_TRUE(eth0_ptr->preference() > eth1_ptr->preference()); } TEST_F(NetworkTest, TestNetworkAdapterTypes) { @@ -842,7 +852,7 @@ TEST_F(NetworkTest, TestConvertIfAddrsNoAddress) { memset(&list, 0, sizeof(list)); list.ifa_name = const_cast("test_iface"); - NetworkManager::NetworkList result; + std::vector> result; PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); manager.StartUpdating(); @@ -859,7 +869,7 @@ TEST_F(NetworkTest, TestConvertIfAddrsMultiAddressesOnOneInterface) { "FFFF:FFFF:FFFF:FFFF::", 0); list = AddIpv6Address(list, if_name, "1000:2000:3000:4000:0:0:0:2", "FFFF:FFFF:FFFF:FFFF::", 0); - NetworkManager::NetworkList result; + std::vector> result; PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); manager.StartUpdating(); @@ -867,7 +877,7 @@ TEST_F(NetworkTest, TestConvertIfAddrsMultiAddressesOnOneInterface) { EXPECT_EQ(1U, result.size()); bool changed; // This ensures we release the objects created in CallConvertIfAddrs. - MergeNetworkList(manager, result, &changed); + MergeNetworkList(manager, std::move(result), &changed); ReleaseIfAddrs(list); } @@ -880,7 +890,7 @@ TEST_F(NetworkTest, TestConvertIfAddrsNotRunning) { list.ifa_addr = &ifa_addr; list.ifa_netmask = &ifa_netmask; - NetworkManager::NetworkList result; + std::vector> result; PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); manager.StartUpdating(); @@ -895,7 +905,8 @@ TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) { std::string ipv6_address = "1000:2000:3000:4000:0:0:0:1"; std::string ipv6_mask = "FFFF:FFFF:FFFF:FFFF::"; PhysicalSocketServer socket_server; - BasicNetworkManager manager_without_monitor(nullptr, &socket_server); + BasicNetworkManager manager_without_monitor(nullptr, &socket_server, + &field_trials_); manager_without_monitor.StartUpdating(); // A network created without a network monitor will get UNKNOWN type. ifaddrs* addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask, @@ -905,7 +916,8 @@ TEST_F(NetworkTest, TestGetAdapterTypeFromNetworkMonitor) { // With the fake network monitor the type should be correctly determined. FakeNetworkMonitorFactory factory; - BasicNetworkManager manager_with_monitor(&factory, &socket_server); + BasicNetworkManager manager_with_monitor(&factory, &socket_server, + &field_trials_); manager_with_monitor.StartUpdating(); // Add the same ipv6 address as before but it has the right network type // detected by the network monitor now. @@ -998,18 +1010,18 @@ TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) { "FFFF:FFFF:FFFF:FFFF::", 0); list = AddIpv6Address(list, if_name2, "1000:2000:3000:4000:0:0:0:2", "FFFF:FFFF:FFFF:FFFF::", 0); - NetworkManager::NetworkList result; + std::vector> result; // Sanity check that both interfaces are included by default. FakeNetworkMonitorFactory factory; PhysicalSocketServer socket_server; - BasicNetworkManager manager(&factory, &socket_server); + BasicNetworkManager manager(&factory, &socket_server, &field_trials_); manager.StartUpdating(); CallConvertIfAddrs(manager, list, /*include_ignored=*/false, &result); EXPECT_EQ(2u, result.size()); bool changed; // This ensures we release the objects created in CallConvertIfAddrs. - MergeNetworkList(manager, result, &changed); + MergeNetworkList(manager, std::move(result), &changed); result.clear(); // Now simulate one interface being unavailable. @@ -1019,7 +1031,7 @@ TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) { EXPECT_EQ(1u, result.size()); EXPECT_EQ(if_name2, result[0]->name()); - MergeNetworkList(manager, result, &changed); + MergeNetworkList(manager, std::move(result), &changed); ReleaseIfAddrs(list); } @@ -1030,7 +1042,7 @@ TEST_F(NetworkTest, TestNetworkMonitorIsAdapterAvailable) { TEST_F(NetworkTest, TestMergeNetworkList) { PhysicalSocketServer socket_server; BasicNetworkManager manager(&socket_server); - NetworkManager::NetworkList list; + std::vector> list; // Create 2 IPAddress classes with only last digit different. IPAddress ip1, ip2; @@ -1038,28 +1050,27 @@ TEST_F(NetworkTest, TestMergeNetworkList) { EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:2", &ip2)); // Create 2 networks with the same prefix and length. - Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); - Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + auto net1 = std::make_unique("em1", "em1", TruncateIP(ip1, 64), 64); + auto net2 = std::make_unique("em1", "em1", TruncateIP(ip1, 64), 64); // Add different IP into each. net1->AddIP(ip1); net2->AddIP(ip2); - list.push_back(net1); - list.push_back(net2); + list.push_back(std::move(net1)); + list.push_back(std::move(net2)); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); - NetworkManager::NetworkList list2; - manager.GetNetworks(&list2); + std::vector list2 = manager.GetNetworks(); // Make sure the resulted networklist has only 1 element and 2 // IPAddresses. EXPECT_EQ(list2.size(), 1uL); EXPECT_EQ(list2[0]->GetIPs().size(), 2uL); - EXPECT_EQ(list2[0]->GetIPs()[0], InterfaceAddress(ip1)); - EXPECT_EQ(list2[0]->GetIPs()[1], InterfaceAddress(ip2)); + EXPECT_THAT(list2[0]->GetIPs(), UnorderedElementsAre(InterfaceAddress(ip1), + InterfaceAddress(ip2))); } // Test that MergeNetworkList successfully detects the change if @@ -1073,37 +1084,40 @@ TEST_F(NetworkTest, TestMergeNetworkListWithInactiveNetworks) { IPAddress(0x00010000U), 16); network1.AddIP(IPAddress(0x12345678)); network2.AddIP(IPAddress(0x00010004)); - NetworkManager::NetworkList list; - Network* net1 = new Network(network1); - list.push_back(net1); + std::vector> list; + auto net1 = std::make_unique(network1); + const Network* const net1_ptr = net1.get(); + list.push_back(std::move(net1)); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); list.clear(); - manager.GetNetworks(&list); - ASSERT_EQ(1U, list.size()); - EXPECT_EQ(net1, list[0]); + + std::vector current = manager.GetNetworks(); + ASSERT_EQ(1U, current.size()); + EXPECT_EQ(net1_ptr, current[0]); list.clear(); - Network* net2 = new Network(network2); - list.push_back(net2); - MergeNetworkList(manager, list, &changed); + auto net2 = std::make_unique(network2); + const Network* const net2_ptr = net2.get(); + list.push_back(std::move(net2)); + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); list.clear(); - manager.GetNetworks(&list); - ASSERT_EQ(1U, list.size()); - EXPECT_EQ(net2, list[0]); + current = manager.GetNetworks(); + ASSERT_EQ(1U, current.size()); + EXPECT_EQ(net2_ptr, current[0]); // Now network1 is inactive. Try to merge it again. list.clear(); - list.push_back(new Network(network1)); - MergeNetworkList(manager, list, &changed); + list.push_back(std::make_unique(network1)); + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); list.clear(); - manager.GetNetworks(&list); - ASSERT_EQ(1U, list.size()); - EXPECT_TRUE(list[0]->active()); - EXPECT_EQ(net1, list[0]); + current = manager.GetNetworks(); + ASSERT_EQ(1U, current.size()); + EXPECT_TRUE(current[0]->active()); + EXPECT_EQ(net1_ptr, current[0]); } // Test that the filtering logic follows the defined ruleset in network.h. @@ -1150,7 +1164,7 @@ TEST_F(NetworkTest, TestIPv6Selection) { TEST_F(NetworkTest, TestNetworkMonitoring) { FakeNetworkMonitorFactory factory; PhysicalSocketServer socket_server; - BasicNetworkManager manager(&factory, &socket_server); + BasicNetworkManager manager(&factory, &socket_server, &field_trials_); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); manager.StartUpdating(); @@ -1181,7 +1195,7 @@ TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) { IPAddress ip; FakeNetworkMonitorFactory factory; PhysicalSocketServer socket_server; - TestBasicNetworkManager manager(&factory, &socket_server); + TestBasicNetworkManager manager(&factory, &socket_server, field_trials_); manager.SignalNetworksChanged.connect(static_cast(this), &NetworkTest::OnNetworksChanged); manager.StartUpdating(); @@ -1189,10 +1203,9 @@ TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) { // Make sure we can query default local address when an address for such // address family exists. - std::vector networks; - manager.GetNetworks(&networks); + std::vector networks = manager.GetNetworks(); EXPECT_TRUE(!networks.empty()); - for (const auto* network : networks) { + for (const Network* network : networks) { if (network->GetBestIP().family() == AF_INET) { EXPECT_TRUE(QueryDefaultLocalAddress(manager, AF_INET) != IPAddress()); } else if (network->GetBestIP().family() == AF_INET6 && @@ -1223,9 +1236,10 @@ TEST_F(NetworkTest, MAYBE_DefaultLocalAddress) { EXPECT_TRUE(IPFromString("abcd::1234:5678:abcd:2222", &ip2)); ipv6_network.AddIP(ip1); ipv6_network.AddIP(ip2); - BasicNetworkManager::NetworkList list(1, new Network(ipv6_network)); + std::vector> list; + list.push_back(std::make_unique(ipv6_network)); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); // If the set default address is not in any network, GetDefaultLocalAddress // should return it. IPAddress ip3; @@ -1250,55 +1264,54 @@ TEST_F(NetworkTest, TestWhenNetworkListChangeReturnsChangedFlag) { IPAddress ip1; EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); - Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + auto net1 = std::make_unique("em1", "em1", TruncateIP(ip1, 64), 64); net1->set_type(ADAPTER_TYPE_CELLULAR_3G); net1->AddIP(ip1); - NetworkManager::NetworkList list; - list.push_back(net1); + std::vector> list; + list.push_back(std::move(net1)); { bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); EXPECT_TRUE(changed); - NetworkManager::NetworkList list2; - manager.GetNetworks(&list2); + std::vector list2 = manager.GetNetworks(); EXPECT_EQ(list2.size(), 1uL); EXPECT_EQ(ADAPTER_TYPE_CELLULAR_3G, list2[0]->type()); } // Modify net1 from 3G to 4G { - Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + auto net2 = + std::make_unique("em1", "em1", TruncateIP(ip1, 64), 64); net2->set_type(ADAPTER_TYPE_CELLULAR_4G); net2->AddIP(ip1); list.clear(); - list.push_back(net2); + list.push_back(std::move(net2)); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); // Change from 3G to 4G shall not trigger OnNetworksChanged, // i.e changed = false. EXPECT_FALSE(changed); - NetworkManager::NetworkList list2; - manager.GetNetworks(&list2); + std::vector list2 = manager.GetNetworks(); ASSERT_EQ(list2.size(), 1uL); EXPECT_EQ(ADAPTER_TYPE_CELLULAR_4G, list2[0]->type()); } // Don't modify. { - Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); + auto net2 = + std::make_unique("em1", "em1", TruncateIP(ip1, 64), 64); net2->set_type(ADAPTER_TYPE_CELLULAR_4G); net2->AddIP(ip1); list.clear(); - list.push_back(net2); + list.push_back(std::move(net2)); bool changed; - MergeNetworkList(manager, list, &changed); + MergeNetworkList(manager, std::move(list), &changed); // No change. EXPECT_FALSE(changed); - NetworkManager::NetworkList list2; - manager.GetNetworks(&list2); + std::vector list2 = manager.GetNetworks(); ASSERT_EQ(list2.size(), 1uL); EXPECT_EQ(ADAPTER_TYPE_CELLULAR_4G, list2[0]->type()); } @@ -1317,8 +1330,7 @@ TEST_F(NetworkTest, IgnoresMACBasedIPv6Address) { ifaddrs* addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask, manager); - BasicNetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); EXPECT_EQ(list.size(), 0u); ReleaseIfAddrs(addr_list); } @@ -1337,8 +1349,7 @@ TEST_F(NetworkTest, WebRTC_AllowMACBasedIPv6Address) { ifaddrs* addr_list = InstallIpv6Network(if_name, ipv6_address, ipv6_mask, manager); - BasicNetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); EXPECT_EQ(list.size(), 1u); ReleaseIfAddrs(addr_list); } @@ -1352,19 +1363,19 @@ TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) { 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; + std::vector> result; // Sanity check that both interfaces are included by default. FakeNetworkMonitorFactory factory; PhysicalSocketServer socket_server; - BasicNetworkManager manager(&factory, &socket_server); + BasicNetworkManager manager(&factory, &socket_server, &field_trials_); 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); + MergeNetworkList(manager, std::move(result), &changed); result.clear(); FakeNetworkMonitor* network_monitor = GetNetworkMonitor(manager); @@ -1389,6 +1400,7 @@ TEST_F(NetworkTest, WebRTC_BindUsingInterfaceName) { TEST_F(NetworkTest, NetworkCostVpn_Default) { IPAddress ip1; EXPECT_TRUE(IPFromString("2400:4030:1:2c00:be30:0:0:1", &ip1)); + webrtc::test::ScopedKeyValueConfig field_trials; Network* net1 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); net1->set_type(ADAPTER_TYPE_VPN); @@ -1397,13 +1409,13 @@ TEST_F(NetworkTest, NetworkCostVpn_Default) { Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); net2->set_type(ADAPTER_TYPE_ETHERNET); - EXPECT_EQ(net1->GetCost(), net2->GetCost()); + EXPECT_EQ(net1->GetCost(field_trials), net2->GetCost(field_trials)); delete net1; delete net2; } TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-AddNetworkCostToVpn/Enabled/"); IPAddress ip1; @@ -1416,13 +1428,13 @@ TEST_F(NetworkTest, NetworkCostVpn_VpnMoreExpensive) { Network* net2 = new Network("em1", "em1", TruncateIP(ip1, 64), 64); net2->set_type(ADAPTER_TYPE_ETHERNET); - EXPECT_GT(net1->GetCost(), net2->GetCost()); + EXPECT_GT(net1->GetCost(field_trials), net2->GetCost(field_trials)); delete net1; delete net2; } TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { - webrtc::test::ScopedFieldTrials field_trials( + webrtc::test::ScopedKeyValueConfig field_trials( "WebRTC-AddNetworkCostToVpn/Enabled/" "WebRTC-UseDifferentiatedCellularCosts/Enabled/"); @@ -1434,7 +1446,8 @@ TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { continue; Network net1("em1", "em1", TruncateIP(ip1, 64), 64); net1.set_type(type); - auto [guess, vpn] = Network::GuessAdapterFromNetworkCost(net1.GetCost()); + auto [guess, vpn] = + Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials)); EXPECT_FALSE(vpn); if (type == rtc::ADAPTER_TYPE_LOOPBACK) { EXPECT_EQ(guess, rtc::ADAPTER_TYPE_ETHERNET); @@ -1450,7 +1463,8 @@ TEST_F(NetworkTest, GuessAdapterFromNetworkCost) { Network net1("em1", "em1", TruncateIP(ip1, 64), 64); net1.set_type(rtc::ADAPTER_TYPE_VPN); net1.set_underlying_type_for_vpn(type); - auto [guess, vpn] = Network::GuessAdapterFromNetworkCost(net1.GetCost()); + auto [guess, vpn] = + Network::GuessAdapterFromNetworkCost(net1.GetCost(field_trials)); EXPECT_TRUE(vpn); if (type == rtc::ADAPTER_TYPE_LOOPBACK) { EXPECT_EQ(guess, rtc::ADAPTER_TYPE_ETHERNET); @@ -1495,8 +1509,7 @@ TEST_F(NetworkTest, VpnListOverrideAdapterType) { auto addr_list = InstallIpv4Network(if_name, "192.168.1.23", "255.255.255.255", manager); - BasicNetworkManager::NetworkList list; - manager.GetNetworks(&list); + std::vector list = manager.GetNetworks(); ASSERT_EQ(1u, list.size()); EXPECT_EQ(ADAPTER_TYPE_VPN, list[0]->type()); EXPECT_EQ(ADAPTER_TYPE_ETHERNET, list[0]->underlying_type_for_vpn()); @@ -1520,4 +1533,93 @@ TEST_F(NetworkTest, HardcodedVpn) { EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(nullptr)); } +TEST(CompareNetworks, IrreflexivityTest) { + // x < x is false + auto network = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network, network)); +} + +TEST(CompareNetworks, AsymmetryTest) { + // x < y and y < x cannot be both true + auto network_a = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_b = std::make_unique( + "test_eth1", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_b, network_a)); + + auto network_c = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345500U), 24); + auto network_d = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_c, network_d)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_c)); +} + +TEST(CompareNetworks, TransitivityTest) { + // x < y and y < z imply x < z + auto network_a = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_b = std::make_unique( + "test_eth1", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_c = std::make_unique( + "test_eth2", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b)); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_b, network_c)); + + auto network_d = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_e = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345700U), 24); + auto network_f = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345800U), 24); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_d, network_e)); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_e, network_f)); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_d, network_f)); + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_c)); +} + +TEST(CompareNetworks, TransitivityOfIncomparabilityTest) { + // x == y and y == z imply x == z, + // where x == y means x < y and y < x are both false + auto network_a = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 23); + auto network_b = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_c = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345700U), 24); + + // network_a < network_b + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_b)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_b, network_a)); + + // network_b < network_c + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_b, network_c)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_c, network_b)); + + // network_a < network_c + EXPECT_TRUE(webrtc_network_internal::CompareNetworks(network_a, network_c)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_c, network_a)); + + auto network_d = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_e = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + auto network_f = std::make_unique( + "test_eth0", "Test Network Adapter 1", IPAddress(0x12345600U), 24); + + // network_d == network_e + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_e)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_e, network_d)); + + // network_e == network_f + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_e, network_f)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_f, network_e)); + + // network_d == network_f + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_d, network_f)); + EXPECT_FALSE(webrtc_network_internal::CompareNetworks(network_f, network_d)); +} + } // namespace rtc diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc index bc10e619eb..a9e5858bca 100644 --- a/rtc_base/openssl_adapter.cc +++ b/rtc_base/openssl_adapter.cc @@ -13,6 +13,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #ifdef OPENSSL_IS_BORINGSSL #include #endif @@ -42,7 +44,7 @@ #include "rtc_base/openssl_identity.h" #endif #include "rtc_base/openssl_utility.h" -#include "rtc_base/string_encode.h" +#include "rtc_base/strings/string_builder.h" #include "rtc_base/thread.h" ////////////////////////////////////////////////////////////////////// @@ -164,6 +166,23 @@ static void LogSslError() { namespace rtc { +namespace webrtc_openssl_adapter_internal { + +// Simple O(n^2) implementation is sufficient for current use case. +std::string StrJoin(const std::vector& list, char delimiter) { + RTC_CHECK(!list.empty()); + StringBuilder sb; + sb << list[0]; + for (size_t i = 1; i < list.size(); i++) { + sb.AppendFormat("%c", delimiter); + sb << list[i]; + } + return sb.Release(); +} +} // namespace webrtc_openssl_adapter_internal + +using webrtc_openssl_adapter_internal::StrJoin; + bool OpenSSLAdapter::InitializeSSL() { if (!SSL_library_init()) return false; @@ -250,11 +269,11 @@ void OpenSSLAdapter::SetRole(SSLRole role) { role_ = role; } -int OpenSSLAdapter::StartSSL(const char* hostname) { +int OpenSSLAdapter::StartSSL(absl::string_view hostname) { if (state_ != SSL_NONE) return -1; - ssl_host_name_ = hostname; + ssl_host_name_.assign(hostname.data(), hostname.size()); if (GetSocket()->GetState() != Socket::CS_CONNECTED) { state_ = SSL_WAIT; @@ -352,7 +371,7 @@ int OpenSSLAdapter::BeginSSL() { } if (!elliptic_curves_.empty()) { - SSL_set1_curves_list(ssl_, rtc::join(elliptic_curves_, ':').c_str()); + SSL_set1_curves_list(ssl_, StrJoin(elliptic_curves_, ':').c_str()); } // Now that the initial config is done, transfer ownership of `bio` to the @@ -420,7 +439,7 @@ int OpenSSLAdapter::ContinueSSL() { return 0; } -void OpenSSLAdapter::Error(const char* context, int err, bool signal) { +void OpenSSLAdapter::Error(absl::string_view context, int err, bool signal) { RTC_LOG(LS_WARNING) << "OpenSSLAdapter::Error(" << context << ", " << err << ")"; state_ = SSL_ERROR; @@ -744,7 +763,7 @@ void OpenSSLAdapter::OnCloseEvent(Socket* socket, int err) { AsyncSocketAdapter::OnCloseEvent(socket, err); } -bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const std::string& host) { +bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, absl::string_view host) { bool is_valid_cert_name = openssl::VerifyPeerCertMatchesHost(ssl, host) && (SSL_get_verify_result(ssl) == X509_V_OK || custom_cert_verifier_status_); diff --git a/rtc_base/openssl_adapter.h b/rtc_base/openssl_adapter.h index 7e1f87b8ab..50ae44d7cc 100644 --- a/rtc_base/openssl_adapter.h +++ b/rtc_base/openssl_adapter.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/message_handler.h" #ifdef OPENSSL_IS_BORINGSSL @@ -36,6 +37,14 @@ namespace rtc { +namespace webrtc_openssl_adapter_internal { + +// Local definition, since absl::StrJoin is not allow-listed. Declared in header +// file only for unittests. +std::string StrJoin(const std::vector& list, char delimiter); + +} // namespace webrtc_openssl_adapter_internal + class OpenSSLAdapter final : public SSLAdapter, public MessageHandlerAutoCleanup { public: @@ -60,7 +69,7 @@ class OpenSSLAdapter final : public SSLAdapter, void SetCertVerifier(SSLCertificateVerifier* ssl_cert_verifier) override; void SetIdentity(std::unique_ptr identity) override; void SetRole(SSLRole role) override; - int StartSSL(const char* hostname) override; + int StartSSL(absl::string_view hostname) override; int Send(const void* pv, size_t cb) override; int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override; int Recv(void* pv, size_t cb, int64_t* timestamp) override; @@ -109,14 +118,14 @@ class OpenSSLAdapter final : public SSLAdapter, int BeginSSL(); int ContinueSSL(); - void Error(const char* context, int err, bool signal = true); + void Error(absl::string_view context, int err, bool signal = true); void Cleanup(); // Return value and arguments have the same meanings as for Send; `error` is // an output parameter filled with the result of SSL_get_error. int DoSslWrite(const void* pv, size_t cb, int* error); void OnMessage(Message* msg) override; - bool SSLPostConnectionCheck(SSL* ssl, const std::string& host); + bool SSLPostConnectionCheck(SSL* ssl, absl::string_view host); #if !defined(NDEBUG) // In debug builds, logs info about the state of the SSL connection. diff --git a/rtc_base/openssl_adapter_unittest.cc b/rtc_base/openssl_adapter_unittest.cc index 0805cba56c..21da777e7a 100644 --- a/rtc_base/openssl_adapter_unittest.cc +++ b/rtc_base/openssl_adapter_unittest.cc @@ -113,4 +113,19 @@ TEST(OpenSSLAdapterFactoryTest, CreateWorksWithCustomVerifier) { EXPECT_NE(simple_adapter, nullptr); } +TEST(StrJoinTest, SingleElement) { + EXPECT_EQ(webrtc_openssl_adapter_internal::StrJoin({"a"}, ','), "a"); +} + +TEST(StrJoinTest, TwoElements) { + EXPECT_EQ(webrtc_openssl_adapter_internal::StrJoin({"first", "second"}, ':'), + "first:second"); +} + +TEST(StrJoinTest, WithEmptyElement) { + EXPECT_EQ( + webrtc_openssl_adapter_internal::StrJoin({"first", "", "second"}, ':'), + "first::second"); +} + } // namespace rtc diff --git a/rtc_base/openssl_certificate.cc b/rtc_base/openssl_certificate.cc index 802787dcfb..faed72b4db 100644 --- a/rtc_base/openssl_certificate.cc +++ b/rtc_base/openssl_certificate.cc @@ -144,8 +144,8 @@ std::unique_ptr OpenSSLCertificate::Generate( } std::unique_ptr OpenSSLCertificate::FromPEMString( - const std::string& pem_string) { - BIO* bio = BIO_new_mem_buf(const_cast(pem_string.c_str()), -1); + absl::string_view pem_string) { + BIO* bio = BIO_new_mem_buf(const_cast(pem_string.data()), -1); if (!bio) { return nullptr; } @@ -208,7 +208,7 @@ bool OpenSSLCertificate::GetSignatureDigestAlgorithm( return true; } -bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm, +bool OpenSSLCertificate::ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const { @@ -216,7 +216,7 @@ bool OpenSSLCertificate::ComputeDigest(const std::string& algorithm, } bool OpenSSLCertificate::ComputeDigest(const X509* x509, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) { diff --git a/rtc_base/openssl_certificate.h b/rtc_base/openssl_certificate.h index b2debbee89..3f1b8c82f9 100644 --- a/rtc_base/openssl_certificate.h +++ b/rtc_base/openssl_certificate.h @@ -37,7 +37,7 @@ class OpenSSLCertificate final : public SSLCertificate { OpenSSLKeyPair* key_pair, const SSLIdentityParams& params); static std::unique_ptr FromPEMString( - const std::string& pem_string); + absl::string_view pem_string); ~OpenSSLCertificate() override; @@ -54,14 +54,14 @@ class OpenSSLCertificate final : public SSLCertificate { bool operator!=(const OpenSSLCertificate& other) const; // Compute the digest of the certificate given algorithm - bool ComputeDigest(const std::string& algorithm, + bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const override; // Compute the digest of a certificate as an X509 * static bool ComputeDigest(const X509* x509, - const std::string& algorithm, + absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length); diff --git a/rtc_base/openssl_digest.cc b/rtc_base/openssl_digest.cc index 1cf5bc09b4..bbf39570f6 100644 --- a/rtc_base/openssl_digest.cc +++ b/rtc_base/openssl_digest.cc @@ -10,12 +10,13 @@ #include "rtc_base/openssl_digest.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" // RTC_DCHECK, RTC_CHECK #include "rtc_base/openssl.h" namespace rtc { -OpenSSLDigest::OpenSSLDigest(const std::string& algorithm) { +OpenSSLDigest::OpenSSLDigest(absl::string_view algorithm) { ctx_ = EVP_MD_CTX_new(); RTC_CHECK(ctx_ != nullptr); EVP_MD_CTX_init(ctx_); @@ -55,7 +56,7 @@ size_t OpenSSLDigest::Finish(void* buf, size_t len) { return md_len; } -bool OpenSSLDigest::GetDigestEVP(const std::string& algorithm, +bool OpenSSLDigest::GetDigestEVP(absl::string_view algorithm, const EVP_MD** mdp) { const EVP_MD* md; if (algorithm == DIGEST_MD5) { @@ -105,8 +106,7 @@ bool OpenSSLDigest::GetDigestName(const EVP_MD* md, std::string* algorithm) { return true; } -bool OpenSSLDigest::GetDigestSize(const std::string& algorithm, - size_t* length) { +bool OpenSSLDigest::GetDigestSize(absl::string_view algorithm, size_t* length) { const EVP_MD* md; if (!GetDigestEVP(algorithm, &md)) return false; diff --git a/rtc_base/openssl_digest.h b/rtc_base/openssl_digest.h index 6da01a0ded..c6cc3bb86d 100644 --- a/rtc_base/openssl_digest.h +++ b/rtc_base/openssl_digest.h @@ -16,6 +16,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/message_digest.h" namespace rtc { @@ -24,7 +25,7 @@ namespace rtc { class OpenSSLDigest final : public MessageDigest { public: // Creates an OpenSSLDigest with `algorithm` as the hash algorithm. - explicit OpenSSLDigest(const std::string& algorithm); + explicit OpenSSLDigest(absl::string_view algorithm); ~OpenSSLDigest() override; // Returns the digest output size (e.g. 16 bytes for MD5). size_t Size() const override; @@ -34,11 +35,11 @@ class OpenSSLDigest final : public MessageDigest { size_t Finish(void* buf, size_t len) override; // Helper function to look up a digest's EVP by name. - static bool GetDigestEVP(const std::string& algorithm, const EVP_MD** md); + static bool GetDigestEVP(absl::string_view algorithm, const EVP_MD** md); // Helper function to look up a digest's name by EVP. static bool GetDigestName(const EVP_MD* md, std::string* algorithm); // Helper function to get the length of a digest. - static bool GetDigestSize(const std::string& algorithm, size_t* len); + static bool GetDigestSize(absl::string_view algorithm, size_t* len); private: EVP_MD_CTX* ctx_ = nullptr; diff --git a/rtc_base/openssl_identity.cc b/rtc_base/openssl_identity.cc index 3794d981ce..186497836d 100644 --- a/rtc_base/openssl_identity.cc +++ b/rtc_base/openssl_identity.cc @@ -70,12 +70,12 @@ std::unique_ptr OpenSSLIdentity::CreateInternal( // static std::unique_ptr OpenSSLIdentity::CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime) { SSLIdentityParams params; params.key_params = key_params; - params.common_name = common_name; + params.common_name = std::string(common_name); time_t now = time(nullptr); params.not_before = now + kCertificateWindowInSeconds; params.not_after = now + certificate_lifetime; @@ -90,8 +90,8 @@ std::unique_ptr OpenSSLIdentity::CreateForTest( } std::unique_ptr OpenSSLIdentity::CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate) { + absl::string_view private_key, + absl::string_view certificate) { std::unique_ptr cert( OpenSSLCertificate::FromPEMString(certificate)); if (!cert) { @@ -110,8 +110,8 @@ std::unique_ptr OpenSSLIdentity::CreateFromPEMStrings( } std::unique_ptr OpenSSLIdentity::CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain) { + absl::string_view private_key, + absl::string_view certificate_chain) { BIO* bio = BIO_new_mem_buf(certificate_chain.data(), rtc::dchecked_cast(certificate_chain.size())); if (!bio) diff --git a/rtc_base/openssl_identity.h b/rtc_base/openssl_identity.h index 63f46b374d..a7372109c3 100644 --- a/rtc_base/openssl_identity.h +++ b/rtc_base/openssl_identity.h @@ -29,17 +29,17 @@ namespace rtc { class OpenSSLIdentity final : public SSLIdentity { public: static std::unique_ptr CreateWithExpiration( - const std::string& common_name, + absl::string_view common_name, const KeyParams& key_params, time_t certificate_lifetime); static std::unique_ptr CreateForTest( const SSLIdentityParams& params); static std::unique_ptr CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate); + absl::string_view private_key, + absl::string_view certificate); static std::unique_ptr CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain); + absl::string_view private_key, + absl::string_view certificate_chain); ~OpenSSLIdentity() override; OpenSSLIdentity(const OpenSSLIdentity&) = delete; diff --git a/rtc_base/openssl_key_pair.cc b/rtc_base/openssl_key_pair.cc index 6ac546e9bb..4c474f2d54 100644 --- a/rtc_base/openssl_key_pair.cc +++ b/rtc_base/openssl_key_pair.cc @@ -13,6 +13,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -103,7 +105,7 @@ std::unique_ptr OpenSSLKeyPair::Generate( } std::unique_ptr OpenSSLKeyPair::FromPrivateKeyPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { BIO* bio = BIO_new_mem_buf(const_cast(pem_string.data()), pem_string.size()); if (!bio) { diff --git a/rtc_base/openssl_key_pair.h b/rtc_base/openssl_key_pair.h index d9a4939a7e..d09bdb0d5e 100644 --- a/rtc_base/openssl_key_pair.h +++ b/rtc_base/openssl_key_pair.h @@ -16,6 +16,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/ssl_identity.h" @@ -34,7 +35,7 @@ class OpenSSLKeyPair final { // Constructs a key pair from the private key PEM string. This must not result // in missing public key parameters. Returns null on error. static std::unique_ptr FromPrivateKeyPEMString( - const std::string& pem_string); + absl::string_view pem_string); ~OpenSSLKeyPair(); diff --git a/rtc_base/openssl_session_cache.cc b/rtc_base/openssl_session_cache.cc index f8fcd473dc..d63724242a 100644 --- a/rtc_base/openssl_session_cache.cc +++ b/rtc_base/openssl_session_cache.cc @@ -10,6 +10,7 @@ #include "rtc_base/openssl_session_cache.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/openssl.h" @@ -30,16 +31,16 @@ OpenSSLSessionCache::~OpenSSLSessionCache() { } SSL_SESSION* OpenSSLSessionCache::LookupSession( - const std::string& hostname) const { + absl::string_view hostname) const { auto it = sessions_.find(hostname); return (it != sessions_.end()) ? it->second : nullptr; } -void OpenSSLSessionCache::AddSession(const std::string& hostname, +void OpenSSLSessionCache::AddSession(absl::string_view hostname, SSL_SESSION* new_session) { SSL_SESSION* old_session = LookupSession(hostname); SSL_SESSION_free(old_session); - sessions_[hostname] = new_session; + sessions_.insert_or_assign(std::string(hostname), new_session); } SSL_CTX* OpenSSLSessionCache::GetSSLContext() const { diff --git a/rtc_base/openssl_session_cache.h b/rtc_base/openssl_session_cache.h index b801ec7b0b..75d8d9a0cf 100644 --- a/rtc_base/openssl_session_cache.h +++ b/rtc_base/openssl_session_cache.h @@ -16,7 +16,9 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/ssl_stream_adapter.h" +#include "rtc_base/string_utils.h" #ifndef OPENSSL_IS_BORINGSSL typedef struct ssl_session_st SSL_SESSION; @@ -40,10 +42,10 @@ class OpenSSLSessionCache final { OpenSSLSessionCache& operator=(const OpenSSLSessionCache&) = delete; // Looks up a session by hostname. The returned SSL_SESSION is not up_refed. - SSL_SESSION* LookupSession(const std::string& hostname) const; + SSL_SESSION* LookupSession(absl::string_view hostname) const; // Adds a session to the cache, and up_refs it. Any existing session with the // same hostname is replaced. - void AddSession(const std::string& hostname, SSL_SESSION* session); + void AddSession(absl::string_view hostname, SSL_SESSION* session); // Returns the true underlying SSL Context that holds these cached sessions. SSL_CTX* GetSSLContext() const; // The SSL Mode tht the OpenSSLSessionCache was constructed with. This cannot @@ -61,7 +63,7 @@ class OpenSSLSessionCache final { // Map of hostnames to SSL_SESSIONs; holds references to the SSL_SESSIONs, // which are cleaned up when the factory is destroyed. // TODO(juberti): Add LRU eviction to keep the cache from growing forever. - std::map sessions_; + std::map sessions_; // The cache should never be copied or assigned directly. }; diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc index dd82e4f061..ee63c70a69 100644 --- a/rtc_base/openssl_stream_adapter.cc +++ b/rtc_base/openssl_stream_adapter.cc @@ -16,6 +16,8 @@ #include #include #include + +#include "absl/strings/string_view.h" #ifndef OPENSSL_IS_BORINGSSL #include #include @@ -327,7 +329,7 @@ void OpenSSLStreamAdapter::SetServerRole(SSLRole role) { } bool OpenSSLStreamAdapter::SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error) { @@ -353,7 +355,7 @@ bool OpenSSLStreamAdapter::SetPeerCertificateDigest( } peer_certificate_digest_value_.SetData(digest_val, digest_len); - peer_certificate_digest_algorithm_ = digest_alg; + peer_certificate_digest_algorithm_ = std::string(digest_alg); if (!peer_cert_chain_) { // Normal case, where the digest is set before we obtain the certificate @@ -445,15 +447,15 @@ bool OpenSSLStreamAdapter::GetSslVersionBytes(int* version) const { } // Key Extractor interface -bool OpenSSLStreamAdapter::ExportKeyingMaterial(const std::string& label, +bool OpenSSLStreamAdapter::ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, uint8_t* result, size_t result_len) { - if (SSL_export_keying_material(ssl_, result, result_len, label.c_str(), - label.length(), const_cast(context), - context_len, use_context) != 1) { + if (SSL_export_keying_material(ssl_, result, result_len, label.data(), + label.length(), context, context_len, + use_context) != 1) { return false; } return true; @@ -839,6 +841,8 @@ void OpenSSLStreamAdapter::SetTimeout(int delay_ms) { RTC_LOG(LS_INFO) << "DTLS retransmission"; } else if (res < 0) { RTC_LOG(LS_INFO) << "DTLSv1_handle_timeout() return -1"; + Error("DTLSv1_handle_timeout", res, -1, true); + return webrtc::TimeDelta::PlusInfinity(); } ContinueSSL(); } else { @@ -954,7 +958,7 @@ int OpenSSLStreamAdapter::ContinueSSL() { return 0; } -void OpenSSLStreamAdapter::Error(const char* context, +void OpenSSLStreamAdapter::Error(absl::string_view context, int err, uint8_t alert, bool signal) { @@ -1263,7 +1267,7 @@ bool OpenSSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) { return false; } -bool OpenSSLStreamAdapter::IsAcceptableCipher(const std::string& cipher, +bool OpenSSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher, KeyType key_type) { if (key_type == KT_RSA) { for (const cipher_list& c : OK_RSA_ciphers) { diff --git a/rtc_base/openssl_stream_adapter.h b/rtc_base/openssl_stream_adapter.h index 236bdfdfea..288ed84047 100644 --- a/rtc_base/openssl_stream_adapter.h +++ b/rtc_base/openssl_stream_adapter.h @@ -19,6 +19,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "rtc_base/buffer.h" #ifdef OPENSSL_IS_BORINGSSL @@ -80,7 +81,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { // Default argument is for compatibility void SetServerRole(SSLRole role = SSL_SERVER) override; bool SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error = nullptr) override; @@ -113,7 +114,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { SSLProtocolVersion GetSslVersion() const override; bool GetSslVersionBytes(int* version) const override; // Key Extractor interface - bool ExportKeyingMaterial(const std::string& label, + bool ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -130,7 +131,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { static bool IsBoringSsl(); static bool IsAcceptableCipher(int cipher, KeyType key_type); - static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); // Use our timeutils.h source of timing in BoringSSL, allowing us to test // using a fake clock. @@ -172,7 +173,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter { // `alert` indicates an alert description (one of the SSL_AD constants) to // send to the remote endpoint when closing the association. If 0, a normal // shutdown will be performed. - void Error(const char* context, int err, uint8_t alert, bool signal); + void Error(absl::string_view context, int err, uint8_t alert, bool signal); void Cleanup(uint8_t alert); // Flush the input buffers by reading left bytes (for DTLS) diff --git a/rtc_base/openssl_utility.cc b/rtc_base/openssl_utility.cc index b5d649ca51..eba3788a94 100644 --- a/rtc_base/openssl_utility.cc +++ b/rtc_base/openssl_utility.cc @@ -9,6 +9,8 @@ */ #include "rtc_base/openssl_utility.h" + +#include "absl/strings/string_view.h" #if defined(WEBRTC_WIN) // Must be included first before openssl headers. #include "rtc_base/win32.h" // NOLINT @@ -184,7 +186,7 @@ bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, } #endif // OPENSSL_IS_BORINGSSL -bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host) { if (host.empty()) { RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate."; return false; @@ -211,8 +213,7 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { return false; } LogCertificates(ssl, x509.get()); - return X509_check_host(x509.get(), host.c_str(), host.size(), 0, nullptr) == - 1; + return X509_check_host(x509.get(), host.data(), host.size(), 0, nullptr) == 1; #else // OPENSSL_IS_BORINGSSL X509* certificate = SSL_get_peer_certificate(ssl); if (certificate == nullptr) { @@ -224,13 +225,13 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) { LogCertificates(ssl, certificate); bool is_valid_cert_name = - X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1; + X509_check_host(certificate, host.data(), host.size(), 0, nullptr) == 1; X509_free(certificate); return is_valid_cert_name; #endif // !defined(OPENSSL_IS_BORINGSSL) } -void LogSSLErrors(const std::string& prefix) { +void LogSSLErrors(absl::string_view prefix) { char error_buf[200]; unsigned long err; // NOLINT diff --git a/rtc_base/openssl_utility.h b/rtc_base/openssl_utility.h index ee29ccd602..dd183c283a 100644 --- a/rtc_base/openssl_utility.h +++ b/rtc_base/openssl_utility.h @@ -15,6 +15,8 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { // The openssl namespace holds static helper methods. All methods related // to OpenSSL that are commonly used and don't require global state should be @@ -35,11 +37,11 @@ bool ParseCertificate(CRYPTO_BUFFER* cert_buffer, // TODO(crbug.com/webrtc/11710): When OS certificate verification is available, // skip compiling this as it adds a dependency on OpenSSL X509 objects, which we // are trying to avoid in favor of CRYPTO_BUFFERs (see crbug.com/webrtc/11410). -bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host); +bool VerifyPeerCertMatchesHost(SSL* ssl, absl::string_view host); // Logs all the errors in the OpenSSL errror queue from the current thread. A // prefix can be provided for context. -void LogSSLErrors(const std::string& prefix); +void LogSSLErrors(absl::string_view prefix); #ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS // Attempt to add the certificates from the loader into the SSL_CTX. False is diff --git a/rtc_base/platform_thread.h b/rtc_base/platform_thread.h index 2c82c02455..3ab2761f43 100644 --- a/rtc_base/platform_thread.h +++ b/rtc_base/platform_thread.h @@ -55,6 +55,10 @@ class PlatformThread final { // removed. PlatformThread(PlatformThread&& rhs); + // Copies won't work since we'd have problems with joinable threads. + PlatformThread(const PlatformThread&) = delete; + PlatformThread& operator=(const PlatformThread&) = delete; + // Moves `rhs` into this, storing an empty state in `rhs`. // TODO(bugs.webrtc.org/12727) Look into if default and move support can be // removed. diff --git a/rtc_base/race_checker.cc b/rtc_base/race_checker.cc index bf9dfdcdaa..f0d4e868c2 100644 --- a/rtc_base/race_checker.cc +++ b/rtc_base/race_checker.cc @@ -25,7 +25,9 @@ RaceChecker::RaceChecker() {} bool RaceChecker::Acquire() const { const PlatformThreadRef current_thread = CurrentThreadRef(); // Set new accessing thread if this is a new use. - if (access_count_++ == 0) + const int current_access_count = access_count_; + access_count_ = access_count_ + 1; + if (current_access_count == 0) accessing_thread_ = current_thread; // If this is being used concurrently this check will fail for the second // thread entering since it won't set the thread. Recursive use of checked @@ -35,7 +37,7 @@ bool RaceChecker::Acquire() const { } void RaceChecker::Release() const { - --access_count_; + access_count_ = access_count_ - 1; } namespace internal { diff --git a/rtc_base/ref_counted_object.h b/rtc_base/ref_counted_object.h index 54aac952ce..8309ba40d5 100644 --- a/rtc_base/ref_counted_object.h +++ b/rtc_base/ref_counted_object.h @@ -142,11 +142,17 @@ class FinalRefCountedObject final : public T { // Note that in some cases, using RefCountedObject directly may still be what's // needed. -// `make_ref_counted` for classes that are convertible to RefCountInterface. -template , - T>::type* = nullptr> +// `make_ref_counted` for abstract classes that are convertible to +// RefCountInterface. The is_abstract requirement rejects classes that inherit +// both RefCountInterface and RefCounted object, which is a a discouraged +// pattern, and would result in double inheritance of RefCountedObject if this +// template was applied. +template < + typename T, + typename... Args, + typename std::enable_if && + std::is_abstract_v, + T>::type* = nullptr> scoped_refptr make_ref_counted(Args&&... args) { return scoped_refptr(new RefCountedObject(std::forward(args)...)); } diff --git a/rtc_base/ref_counted_object_unittest.cc b/rtc_base/ref_counted_object_unittest.cc index 6b794e63f9..ab051d4f8a 100644 --- a/rtc_base/ref_counted_object_unittest.cc +++ b/rtc_base/ref_counted_object_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/scoped_refptr.h" #include "rtc_base/ref_count.h" #include "test/gtest.h" @@ -52,7 +53,7 @@ class RefClassWithRvalue : public RefCountInterface { class RefClassWithMixedValues : public RefCountInterface { public: - RefClassWithMixedValues(std::unique_ptr a, int b, const std::string& c) + RefClassWithMixedValues(std::unique_ptr a, int b, absl::string_view c) : a_(std::move(a)), b_(b), c_(c) {} protected: diff --git a/rtc_base/rtc_certificate.h b/rtc_base/rtc_certificate.h index 0102c4f98c..67c5c29a89 100644 --- a/rtc_base/rtc_certificate.h +++ b/rtc_base/rtc_certificate.h @@ -17,6 +17,7 @@ #include #include "absl/base/attributes.h" +#include "absl/strings/string_view.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "rtc_base/system/rtc_export.h" @@ -35,8 +36,8 @@ class SSLIdentity; // the string representations used by OpenSSL. class RTCCertificatePEM { public: - RTCCertificatePEM(const std::string& private_key, - const std::string& certificate) + RTCCertificatePEM(absl::string_view private_key, + absl::string_view certificate) : private_key_(private_key), certificate_(certificate) {} const std::string& private_key() const { return private_key_; } diff --git a/rtc_base/socket_adapters.cc b/rtc_base/socket_adapters.cc index 0bd6efad3e..4ec93ae3e9 100644 --- a/rtc_base/socket_adapters.cc +++ b/rtc_base/socket_adapters.cc @@ -12,15 +12,17 @@ #pragma warning(disable : 4786) #endif +#include "rtc_base/socket_adapters.h" + #include #include "absl/strings/match.h" +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/byte_buffer.h" #include "rtc_base/checks.h" #include "rtc_base/http_common.h" #include "rtc_base/logging.h" -#include "rtc_base/socket_adapters.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/zero_memory.h" @@ -217,9 +219,9 @@ void AsyncSSLSocket::ProcessInput(char* data, size_t* len) { /////////////////////////////////////////////////////////////////////////////// AsyncHttpsProxySocket::AsyncHttpsProxySocket(Socket* socket, - const std::string& user_agent, + absl::string_view user_agent, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password) : BufferedReadAdapter(socket, 1024), proxy_(proxy), @@ -409,8 +411,9 @@ void AsyncHttpsProxySocket::ProcessLine(char* data, size_t len) { } else if ((state_ == PS_AUTHENTICATE) && absl::StartsWithIgnoreCase(data, "Proxy-Authenticate:")) { std::string response, auth_method; - switch (HttpAuthenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_, - pass_, context_, response, auth_method)) { + switch (HttpAuthenticate(absl::string_view(data + 19, len - 19), proxy_, + "CONNECT", "/", user_, pass_, context_, response, + auth_method)) { case HAR_IGNORE: RTC_LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method; if (!unknown_mechanisms_.empty()) @@ -470,7 +473,7 @@ void AsyncHttpsProxySocket::Error(int error) { AsyncSocksProxySocket::AsyncSocksProxySocket(Socket* socket, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password) : BufferedReadAdapter(socket, 1024), state_(SS_ERROR), diff --git a/rtc_base/socket_adapters.h b/rtc_base/socket_adapters.h index 55f62115d3..e78ee18a27 100644 --- a/rtc_base/socket_adapters.h +++ b/rtc_base/socket_adapters.h @@ -13,6 +13,7 @@ #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "rtc_base/async_socket.h" #include "rtc_base/crypt_string.h" @@ -82,9 +83,9 @@ class AsyncSSLSocket : public BufferedReadAdapter { class AsyncHttpsProxySocket : public BufferedReadAdapter { public: AsyncHttpsProxySocket(Socket* socket, - const std::string& user_agent, + absl::string_view user_agent, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password); ~AsyncHttpsProxySocket() override; @@ -143,7 +144,7 @@ class AsyncSocksProxySocket : public BufferedReadAdapter { public: AsyncSocksProxySocket(Socket* socket, const SocketAddress& proxy, - const std::string& username, + absl::string_view username, const CryptString& password); ~AsyncSocksProxySocket() override; diff --git a/rtc_base/socket_address.cc b/rtc_base/socket_address.cc index 2996ede9d2..93d6860a70 100644 --- a/rtc_base/socket_address.cc +++ b/rtc_base/socket_address.cc @@ -10,6 +10,7 @@ #include "rtc_base/socket_address.h" +#include "absl/strings/string_view.h" #include "rtc_base/numerics/safe_conversions.h" #if defined(WEBRTC_POSIX) @@ -43,7 +44,7 @@ SocketAddress::SocketAddress() { Clear(); } -SocketAddress::SocketAddress(const std::string& hostname, int port) { +SocketAddress::SocketAddress(absl::string_view hostname, int port) { SetIP(hostname); SetPort(port); } @@ -101,8 +102,8 @@ void SocketAddress::SetIP(const IPAddress& ip) { scope_id_ = 0; } -void SocketAddress::SetIP(const std::string& hostname) { - hostname_ = hostname; +void SocketAddress::SetIP(absl::string_view hostname) { + hostname_ = std::string(hostname); literal_ = IPFromString(hostname, &ip_); if (!literal_) { ip_ = IPAddress(); @@ -188,23 +189,24 @@ std::string SocketAddress::ToResolvedSensitiveString() const { return sb.str(); } -bool SocketAddress::FromString(const std::string& str) { +bool SocketAddress::FromString(absl::string_view str) { if (str.at(0) == '[') { - std::string::size_type closebracket = str.rfind(']'); - if (closebracket != std::string::npos) { - std::string::size_type colon = str.find(':', closebracket); - if (colon != std::string::npos && colon > closebracket) { - SetPort(strtoul(str.substr(colon + 1).c_str(), nullptr, 10)); + absl::string_view::size_type closebracket = str.rfind(']'); + if (closebracket != absl::string_view::npos) { + absl::string_view::size_type colon = str.find(':', closebracket); + if (colon != absl::string_view::npos && colon > closebracket) { + SetPort( + strtoul(std::string(str.substr(colon + 1)).c_str(), nullptr, 10)); SetIP(str.substr(1, closebracket - 1)); } else { return false; } } } else { - std::string::size_type pos = str.find(':'); - if (std::string::npos == pos) + absl::string_view::size_type pos = str.find(':'); + if (absl::string_view::npos == pos) return false; - SetPort(strtoul(str.substr(pos + 1).c_str(), nullptr, 10)); + SetPort(strtoul(std::string(str.substr(pos + 1)).c_str(), nullptr, 10)); SetIP(str.substr(0, pos)); } return true; diff --git a/rtc_base/socket_address.h b/rtc_base/socket_address.h index 570a71281e..99e14d8eab 100644 --- a/rtc_base/socket_address.h +++ b/rtc_base/socket_address.h @@ -12,6 +12,8 @@ #define RTC_BASE_SOCKET_ADDRESS_H_ #include + +#include "absl/strings/string_view.h" #ifdef WEBRTC_UNIT_TEST #include // no-presubmit-check TODO(webrtc:8982) #endif // WEBRTC_UNIT_TEST @@ -34,7 +36,7 @@ class RTC_EXPORT SocketAddress { // Creates the address with the given host and port. Host may be a // literal IP string or a hostname to be resolved later. // DCHECKs that port is in valid range (0 to 2^16-1). - SocketAddress(const std::string& hostname, int port); + SocketAddress(absl::string_view hostname, int port); // Creates the address with the given IP and port. // IP is given as an integer in host byte order. V4 only, to be deprecated. @@ -69,7 +71,7 @@ class RTC_EXPORT SocketAddress { // Changes the hostname of this address to the given one. // Does not resolve the address; use Resolve to do so. - void SetIP(const std::string& hostname); + void SetIP(absl::string_view hostname); // Sets the IP address while retaining the hostname. Useful for bypassing // DNS for a pre-resolved IP. @@ -129,7 +131,7 @@ class RTC_EXPORT SocketAddress { std::string ToResolvedSensitiveString() const; // Parses hostname:port and [hostname]:port. - bool FromString(const std::string& str); + bool FromString(absl::string_view str); #ifdef WEBRTC_UNIT_TEST inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982) diff --git a/rtc_base/socket_unittest.cc b/rtc_base/socket_unittest.cc index 01a2bed26d..0b9c2f2c58 100644 --- a/rtc_base/socket_unittest.cc +++ b/rtc_base/socket_unittest.cc @@ -17,6 +17,7 @@ #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/async_udp_socket.h" @@ -287,7 +288,7 @@ void SocketTest::ConnectInternal(const IPAddress& loopback) { } void SocketTest::ConnectWithDnsLookupInternal(const IPAddress& loopback, - const std::string& host) { + absl::string_view host) { StreamSink sink; SocketAddress accept_addr; diff --git a/rtc_base/socket_unittest.h b/rtc_base/socket_unittest.h index 772df635d7..20ef003a80 100644 --- a/rtc_base/socket_unittest.h +++ b/rtc_base/socket_unittest.h @@ -11,6 +11,7 @@ #ifndef RTC_BASE_SOCKET_UNITTEST_H_ #define RTC_BASE_SOCKET_UNITTEST_H_ +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "rtc_base/thread.h" @@ -74,7 +75,7 @@ class SocketTest : public ::testing::Test { private: void ConnectInternal(const IPAddress& loopback); void ConnectWithDnsLookupInternal(const IPAddress& loopback, - const std::string& host); + absl::string_view host); void ConnectFailInternal(const IPAddress& loopback); void ConnectWithDnsLookupFailInternal(const IPAddress& loopback); diff --git a/rtc_base/ssl_adapter.h b/rtc_base/ssl_adapter.h index 8f98141651..4b8b9c74e0 100644 --- a/rtc_base/ssl_adapter.h +++ b/rtc_base/ssl_adapter.h @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/async_socket.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" @@ -89,7 +90,7 @@ class SSLAdapter : public AsyncSocketAdapter { // StartSSL returns 0 if successful. // If StartSSL is called while the socket is closed or connecting, the SSL // negotiation will begin as soon as the socket connects. - virtual int StartSSL(const char* hostname) = 0; + virtual int StartSSL(absl::string_view hostname) = 0; // When an SSLAdapterFactory is used, an SSLAdapter may be used to resume // a previous SSL session, which results in an abbreviated handshake. diff --git a/rtc_base/ssl_adapter_unittest.cc b/rtc_base/ssl_adapter_unittest.cc index 2b55211086..cd63249190 100644 --- a/rtc_base/ssl_adapter_unittest.cc +++ b/rtc_base/ssl_adapter_unittest.cc @@ -8,16 +8,18 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_adapter.h" + #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/gunit.h" #include "rtc_base/ip_address.h" #include "rtc_base/message_digest.h" #include "rtc_base/socket_stream.h" -#include "rtc_base/ssl_adapter.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" @@ -99,7 +101,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { const std::string& GetReceivedData() const { return data_; } - int Connect(const std::string& hostname, const rtc::SocketAddress& address) { + int Connect(absl::string_view hostname, const rtc::SocketAddress& address) { RTC_LOG(LS_INFO) << "Initiating connection with " << address.ToString(); int rv = ssl_adapter_->Connect(address); @@ -108,7 +110,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { RTC_LOG(LS_INFO) << "Starting " << GetSSLProtocolName(ssl_mode_) << " handshake with " << hostname; - if (ssl_adapter_->StartSSL(hostname.c_str()) != 0) { + if (ssl_adapter_->StartSSL(hostname) != 0) { return -1; } } @@ -118,7 +120,7 @@ class SSLAdapterTestDummyClient : public sigslot::has_slots<> { int Close() { return ssl_adapter_->Close(); } - int Send(const std::string& message) { + int Send(absl::string_view message) { RTC_LOG(LS_INFO) << "Client sending '" << message << "'"; return ssl_adapter_->Send(message.data(), message.length()); @@ -189,7 +191,7 @@ class SSLAdapterTestDummyServer : public sigslot::has_slots<> { const std::string& GetReceivedData() const { return data_; } - int Send(const std::string& message) { + int Send(absl::string_view message) { if (ssl_stream_adapter_ == nullptr || ssl_stream_adapter_->GetState() != rtc::SS_OPEN) { // No connection yet. @@ -363,7 +365,7 @@ class SSLAdapterTestBase : public ::testing::Test, public sigslot::has_slots<> { } } - void TestTransfer(const std::string& message) { + void TestTransfer(absl::string_view message) { int rv; rv = client_->Send(message); diff --git a/rtc_base/ssl_certificate.cc b/rtc_base/ssl_certificate.cc index ed42998353..ddb1524f76 100644 --- a/rtc_base/ssl_certificate.cc +++ b/rtc_base/ssl_certificate.cc @@ -15,6 +15,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/openssl.h" #ifdef OPENSSL_IS_BORINGSSL @@ -121,7 +122,7 @@ std::unique_ptr SSLCertChain::GetStats() const { // static std::unique_ptr SSLCertificate::FromPEMString( - const std::string& pem_string) { + absl::string_view pem_string) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLCertificate::FromPEMString(pem_string); #else diff --git a/rtc_base/ssl_certificate.h b/rtc_base/ssl_certificate.h index d0e60ee9cc..77fbba3e9e 100644 --- a/rtc_base/ssl_certificate.h +++ b/rtc_base/ssl_certificate.h @@ -22,6 +22,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/buffer.h" #include "rtc_base/system/rtc_export.h" @@ -55,7 +56,7 @@ class RTC_EXPORT SSLCertificate { // stored in *pem_length if it is non-null, and only if // parsing was successful. static std::unique_ptr FromPEMString( - const std::string& pem_string); + absl::string_view pem_string); virtual ~SSLCertificate() = default; // Returns a new SSLCertificate object instance wrapping the same @@ -73,7 +74,7 @@ class RTC_EXPORT SSLCertificate { virtual bool GetSignatureDigestAlgorithm(std::string* algorithm) const = 0; // Compute the digest of the certificate given algorithm - virtual bool ComputeDigest(const std::string& algorithm, + virtual bool ComputeDigest(absl::string_view algorithm, unsigned char* digest, size_t size, size_t* length) const = 0; diff --git a/rtc_base/ssl_fingerprint.cc b/rtc_base/ssl_fingerprint.cc index 358402eb03..a43bb159c3 100644 --- a/rtc_base/ssl_fingerprint.cc +++ b/rtc_base/ssl_fingerprint.cc @@ -11,11 +11,14 @@ #include "rtc_base/ssl_fingerprint.h" #include + #include #include #include #include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/rtc_certificate.h" @@ -25,19 +28,19 @@ namespace rtc { -SSLFingerprint* SSLFingerprint::Create(const std::string& algorithm, +SSLFingerprint* SSLFingerprint::Create(absl::string_view algorithm, const rtc::SSLIdentity* identity) { return CreateUnique(algorithm, *identity).release(); } std::unique_ptr SSLFingerprint::CreateUnique( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLIdentity& identity) { return Create(algorithm, identity.certificate()); } std::unique_ptr SSLFingerprint::Create( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLCertificate& cert) { uint8_t digest_val[64]; size_t digest_len; @@ -51,14 +54,14 @@ std::unique_ptr SSLFingerprint::Create( } SSLFingerprint* SSLFingerprint::CreateFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint) { + absl::string_view algorithm, + absl::string_view fingerprint) { return CreateUniqueFromRfc4572(algorithm, fingerprint).release(); } std::unique_ptr SSLFingerprint::CreateUniqueFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint) { + absl::string_view algorithm, + absl::string_view fingerprint) { if (algorithm.empty() || !rtc::IsFips180DigestAlgorithm(algorithm)) return nullptr; @@ -66,8 +69,8 @@ std::unique_ptr SSLFingerprint::CreateUniqueFromRfc4572( return nullptr; char value[rtc::MessageDigest::kMaxSize]; - size_t value_len = rtc::hex_decode_with_delimiter( - value, sizeof(value), fingerprint.c_str(), fingerprint.length(), ':'); + size_t value_len = + rtc::hex_decode_with_delimiter(ArrayView(value), fingerprint, ':'); if (!value_len) return nullptr; @@ -94,11 +97,11 @@ std::unique_ptr SSLFingerprint::CreateFromCertificate( return fingerprint; } -SSLFingerprint::SSLFingerprint(const std::string& algorithm, +SSLFingerprint::SSLFingerprint(absl::string_view algorithm, ArrayView digest_view) : algorithm(algorithm), digest(digest_view.data(), digest_view.size()) {} -SSLFingerprint::SSLFingerprint(const std::string& algorithm, +SSLFingerprint::SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, size_t digest_len) : SSLFingerprint(algorithm, MakeArrayView(digest_in, digest_len)) {} @@ -108,8 +111,8 @@ bool SSLFingerprint::operator==(const SSLFingerprint& other) const { } std::string SSLFingerprint::GetRfc4572Fingerprint() const { - std::string fingerprint = - rtc::hex_encode_with_delimiter(digest.data(), digest.size(), ':'); + std::string fingerprint = rtc::hex_encode_with_delimiter( + absl::string_view(digest.data(), digest.size()), ':'); absl::c_transform(fingerprint, fingerprint.begin(), ::toupper); return fingerprint; } diff --git a/rtc_base/ssl_fingerprint.h b/rtc_base/ssl_fingerprint.h index add3ab7911..cfa26dd433 100644 --- a/rtc_base/ssl_fingerprint.h +++ b/rtc_base/ssl_fingerprint.h @@ -13,8 +13,10 @@ #include #include + #include +#include "absl/strings/string_view.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/system/rtc_export.h" @@ -26,34 +28,34 @@ class SSLIdentity; struct RTC_EXPORT SSLFingerprint { // TODO(steveanton): Remove once downstream projects have moved off of this. - static SSLFingerprint* Create(const std::string& algorithm, + static SSLFingerprint* Create(absl::string_view algorithm, const rtc::SSLIdentity* identity); // TODO(steveanton): Rename to Create once projects have migrated. static std::unique_ptr CreateUnique( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLIdentity& identity); static std::unique_ptr Create( - const std::string& algorithm, + absl::string_view algorithm, const rtc::SSLCertificate& cert); // TODO(steveanton): Remove once downstream projects have moved off of this. - static SSLFingerprint* CreateFromRfc4572(const std::string& algorithm, - const std::string& fingerprint); + static SSLFingerprint* CreateFromRfc4572(absl::string_view algorithm, + absl::string_view fingerprint); // TODO(steveanton): Rename to CreateFromRfc4572 once projects have migrated. static std::unique_ptr CreateUniqueFromRfc4572( - const std::string& algorithm, - const std::string& fingerprint); + absl::string_view algorithm, + absl::string_view fingerprint); // Creates a fingerprint from a certificate, using the same digest algorithm // as the certificate's signature. static std::unique_ptr CreateFromCertificate( const RTCCertificate& cert); - SSLFingerprint(const std::string& algorithm, + SSLFingerprint(absl::string_view algorithm, ArrayView digest_view); // TODO(steveanton): Remove once downstream projects have moved off of this. - SSLFingerprint(const std::string& algorithm, + SSLFingerprint(absl::string_view algorithm, const uint8_t* digest_in, size_t digest_len); diff --git a/rtc_base/ssl_identity.cc b/rtc_base/ssl_identity.cc index 81cf1d78a3..984979a8a6 100644 --- a/rtc_base/ssl_identity.cc +++ b/rtc_base/ssl_identity.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #ifdef OPENSSL_IS_BORINGSSL #include "rtc_base/boringssl_identity.h" @@ -169,30 +170,32 @@ KeyType IntKeyTypeFamilyToKeyType(int key_type_family) { // SSLIdentity ////////////////////////////////////////////////////////////////////// -bool SSLIdentity::PemToDer(const std::string& pem_type, - const std::string& pem_string, +bool SSLIdentity::PemToDer(absl::string_view pem_type, + absl::string_view pem_string, std::string* der) { // Find the inner body. We need this to fulfill the contract of returning // pem_length. - size_t header = pem_string.find("-----BEGIN " + pem_type + "-----"); - if (header == std::string::npos) { + std::string pem_type_str = std::string(pem_type); + size_t header = pem_string.find("-----BEGIN " + pem_type_str + "-----"); + if (header == absl::string_view::npos) { return false; } size_t body = pem_string.find('\n', header); - if (body == std::string::npos) { + if (body == absl::string_view::npos) { return false; } - size_t trailer = pem_string.find("-----END " + pem_type + "-----"); - if (trailer == std::string::npos) { + size_t trailer = pem_string.find("-----END " + pem_type_str + "-----"); + if (trailer == absl::string_view::npos) { return false; } - std::string inner = pem_string.substr(body + 1, trailer - (body + 1)); + std::string inner = + std::string(pem_string.substr(body + 1, trailer - (body + 1))); *der = Base64::Decode(inner, Base64::DO_PARSE_WHITE | Base64::DO_PAD_ANY | Base64::DO_TERM_BUFFER); return true; } -std::string SSLIdentity::DerToPem(const std::string& pem_type, +std::string SSLIdentity::DerToPem(absl::string_view pem_type, const unsigned char* data, size_t length) { rtc::StringBuilder result; @@ -214,7 +217,7 @@ std::string SSLIdentity::DerToPem(const std::string& pem_type, } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, const KeyParams& key_param, time_t certificate_lifetime) { #ifdef OPENSSL_IS_BORINGSSL @@ -227,13 +230,13 @@ std::unique_ptr SSLIdentity::Create(const std::string& common_name, } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, const KeyParams& key_param) { return Create(common_name, key_param, kDefaultCertificateLifetimeInSeconds); } // static -std::unique_ptr SSLIdentity::Create(const std::string& common_name, +std::unique_ptr SSLIdentity::Create(absl::string_view common_name, KeyType key_type) { return Create(common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds); @@ -252,8 +255,8 @@ std::unique_ptr SSLIdentity::CreateForTest( // Construct an identity from a private key and a certificate. // static std::unique_ptr SSLIdentity::CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate) { + absl::string_view private_key, + absl::string_view certificate) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLIdentity::CreateFromPEMStrings(private_key, certificate); #else @@ -264,8 +267,8 @@ std::unique_ptr SSLIdentity::CreateFromPEMStrings( // Construct an identity from a private key and a certificate chain. // static std::unique_ptr SSLIdentity::CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain) { + absl::string_view private_key, + absl::string_view certificate_chain) { #ifdef OPENSSL_IS_BORINGSSL return BoringSSLIdentity::CreateFromPEMChainStrings(private_key, certificate_chain); diff --git a/rtc_base/ssl_identity.h b/rtc_base/ssl_identity.h index 78d1ec12b7..a0119bb1c4 100644 --- a/rtc_base/ssl_identity.h +++ b/rtc_base/ssl_identity.h @@ -14,10 +14,12 @@ #define RTC_BASE_SSL_IDENTITY_H_ #include + #include #include #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -108,12 +110,12 @@ class RTC_EXPORT SSLIdentity { // should be a non-negative number. // Returns null on failure. // Caller is responsible for freeing the returned object. - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, const KeyParams& key_param, time_t certificate_lifetime); - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, const KeyParams& key_param); - static std::unique_ptr Create(const std::string& common_name, + static std::unique_ptr Create(absl::string_view common_name, KeyType key_type); // Allows fine-grained control over expiration time. @@ -122,13 +124,13 @@ class RTC_EXPORT SSLIdentity { // Construct an identity from a private key and a certificate. static std::unique_ptr CreateFromPEMStrings( - const std::string& private_key, - const std::string& certificate); + absl::string_view private_key, + absl::string_view certificate); // Construct an identity from a private key and a certificate chain. static std::unique_ptr CreateFromPEMChainStrings( - const std::string& private_key, - const std::string& certificate_chain); + absl::string_view private_key, + absl::string_view certificate_chain); virtual ~SSLIdentity() {} @@ -144,10 +146,10 @@ class RTC_EXPORT SSLIdentity { virtual std::string PublicKeyToPEMString() const = 0; // Helpers for parsing converting between PEM and DER format. - static bool PemToDer(const std::string& pem_type, - const std::string& pem_string, + static bool PemToDer(absl::string_view pem_type, + absl::string_view pem_string, std::string* der); - static std::string DerToPem(const std::string& pem_type, + static std::string DerToPem(absl::string_view pem_type, const unsigned char* data, size_t length); diff --git a/rtc_base/ssl_identity_unittest.cc b/rtc_base/ssl_identity_unittest.cc index 53f4a2ad10..1f0278ac71 100644 --- a/rtc_base/ssl_identity_unittest.cc +++ b/rtc_base/ssl_identity_unittest.cc @@ -8,19 +8,22 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_identity.h" + #include + #include #include #include #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/fake_ssl_identity.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" #include "rtc_base/ssl_fingerprint.h" -#include "rtc_base/ssl_identity.h" #include "test/gtest.h" using rtc::SSLIdentity; @@ -236,7 +239,7 @@ class SSLIdentityTest : public ::testing::Test { void TestDigestHelper(DigestType digest, const SSLIdentity* identity, - const std::string& algorithm, + absl::string_view algorithm, size_t expected_len) { DigestType digest1; size_t digest_len; @@ -258,7 +261,7 @@ class SSLIdentityTest : public ::testing::Test { EXPECT_EQ(0, memcmp(digest, digest1, expected_len)); } - void TestDigestForGeneratedCert(const std::string& algorithm, + void TestDigestForGeneratedCert(absl::string_view algorithm, size_t expected_len) { DigestType digest[4]; @@ -281,7 +284,7 @@ class SSLIdentityTest : public ::testing::Test { } } - void TestDigestForFixedCert(const std::string& algorithm, + void TestDigestForFixedCert(absl::string_view algorithm, size_t expected_len, const unsigned char* expected_digest) { bool rv; diff --git a/rtc_base/ssl_stream_adapter.cc b/rtc_base/ssl_stream_adapter.cc index b805fdc6c3..4b60d6d7b1 100644 --- a/rtc_base/ssl_stream_adapter.cc +++ b/rtc_base/ssl_stream_adapter.cc @@ -11,6 +11,7 @@ #include "rtc_base/ssl_stream_adapter.h" #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/openssl_stream_adapter.h" /////////////////////////////////////////////////////////////////////////////// @@ -39,7 +40,7 @@ std::string SrtpCryptoSuiteToName(int crypto_suite) { } } -int SrtpCryptoSuiteFromName(const std::string& crypto_suite) { +int SrtpCryptoSuiteFromName(absl::string_view crypto_suite) { if (crypto_suite == kCsAesCm128HmacSha1_32) return kSrtpAes128CmSha1_32; if (crypto_suite == kCsAesCm128HmacSha1_80) @@ -85,7 +86,7 @@ bool IsGcmCryptoSuite(int crypto_suite) { crypto_suite == kSrtpAeadAes128Gcm); } -bool IsGcmCryptoSuiteName(const std::string& crypto_suite) { +bool IsGcmCryptoSuiteName(absl::string_view crypto_suite) { return (crypto_suite == kCsAeadAes256Gcm || crypto_suite == kCsAeadAes128Gcm); } @@ -98,7 +99,7 @@ bool SSLStreamAdapter::GetSslCipherSuite(int* cipher_suite) { return false; } -bool SSLStreamAdapter::ExportKeyingMaterial(const std::string& label, +bool SSLStreamAdapter::ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -122,7 +123,7 @@ bool SSLStreamAdapter::IsBoringSsl() { bool SSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) { return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type); } -bool SSLStreamAdapter::IsAcceptableCipher(const std::string& cipher, +bool SSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher, KeyType key_type) { return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type); } diff --git a/rtc_base/ssl_stream_adapter.h b/rtc_base/ssl_stream_adapter.h index 618ffca4d0..e68870c747 100644 --- a/rtc_base/ssl_stream_adapter.h +++ b/rtc_base/ssl_stream_adapter.h @@ -13,11 +13,13 @@ #include #include + #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/ssl_certificate.h" #include "rtc_base/ssl_identity.h" #include "rtc_base/stream.h" @@ -53,7 +55,7 @@ extern const char kCsAeadAes256Gcm[]; std::string SrtpCryptoSuiteToName(int crypto_suite); // The reverse of above conversion. -int SrtpCryptoSuiteFromName(const std::string& crypto_suite); +int SrtpCryptoSuiteFromName(absl::string_view crypto_suite); // Get key length and salt length for given crypto suite. Returns true for // valid suites, otherwise false. @@ -65,7 +67,7 @@ bool GetSrtpKeyAndSaltLengths(int crypto_suite, bool IsGcmCryptoSuite(int crypto_suite); // Returns true if the given crypto suite name uses a GCM cipher. -bool IsGcmCryptoSuiteName(const std::string& crypto_suite); +bool IsGcmCryptoSuiteName(absl::string_view crypto_suite); // SSLStreamAdapter : A StreamInterfaceAdapter that does SSL/TLS. // After SSL has been started, the stream will only open on successful @@ -176,7 +178,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // Returns true if successful. // `error` is optional and provides more information about the failure. virtual bool SetPeerCertificateDigest( - const std::string& digest_alg, + absl::string_view digest_alg, const unsigned char* digest_val, size_t digest_len, SSLPeerCertificateDigestError* error = nullptr) = 0; @@ -208,7 +210,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // zero-length ones). // result -- where to put the computed value // result_len -- the length of the computed value - virtual bool ExportKeyingMaterial(const std::string& label, + virtual bool ExportKeyingMaterial(absl::string_view label, const uint8_t* context, size_t context_len, bool use_context, @@ -233,7 +235,7 @@ class SSLStreamAdapter : public StreamInterface, public sigslot::has_slots<> { // Returns true iff the supplied cipher is deemed to be strong. // TODO(torbjorng): Consider removing the KeyType argument. static bool IsAcceptableCipher(int cipher, KeyType key_type); - static bool IsAcceptableCipher(const std::string& cipher, KeyType key_type); + static bool IsAcceptableCipher(absl::string_view cipher, KeyType key_type); // TODO(guoweis): Move this away from a static class method. Currently this is // introduced such that any caller could depend on sslstreamadapter.h without diff --git a/rtc_base/ssl_stream_adapter_unittest.cc b/rtc_base/ssl_stream_adapter_unittest.cc index f92958dd86..2305bcea71 100644 --- a/rtc_base/ssl_stream_adapter_unittest.cc +++ b/rtc_base/ssl_stream_adapter_unittest.cc @@ -8,12 +8,15 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include "rtc_base/ssl_stream_adapter.h" + #include #include #include #include #include "absl/memory/memory.h" +#include "absl/strings/string_view.h" #include "rtc_base/buffer_queue.h" #include "rtc_base/checks.h" #include "rtc_base/gunit.h" @@ -24,7 +27,6 @@ #include "rtc_base/openssl_stream_adapter.h" #include "rtc_base/ssl_adapter.h" #include "rtc_base/ssl_identity.h" -#include "rtc_base/ssl_stream_adapter.h" #include "rtc_base/stream.h" #include "rtc_base/task_utils/pending_task_safety_flag.h" #include "rtc_base/task_utils/to_queued_task.h" @@ -147,7 +149,7 @@ class SSLDummyStreamBase : public rtc::StreamInterface, public sigslot::has_slots<> { public: SSLDummyStreamBase(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, rtc::StreamInterface* in, rtc::StreamInterface* out) : test_base_(test), side_(side), in_(in), out_(out), first_packet_(true) { @@ -235,7 +237,7 @@ class SSLDummyStreamBase : public rtc::StreamInterface, class SSLDummyStreamTLS : public SSLDummyStreamBase { public: SSLDummyStreamTLS(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, rtc::FifoBuffer* in, rtc::FifoBuffer* out) : SSLDummyStreamBase(test, side, in, out) {} @@ -303,7 +305,7 @@ class BufferQueueStream : public rtc::StreamInterface { class SSLDummyStreamDTLS : public SSLDummyStreamBase { public: SSLDummyStreamDTLS(SSLStreamAdapterTestBase* test, - const std::string& side, + absl::string_view side, BufferQueueStream* in, BufferQueueStream* out) : SSLDummyStreamBase(test, side, in, out) {} @@ -317,8 +319,8 @@ class SSLStreamAdapterTestBase : public ::testing::Test, public sigslot::has_slots<> { public: SSLStreamAdapterTestBase( - const std::string& client_cert_pem, - const std::string& client_private_key_pem, + absl::string_view client_cert_pem, + absl::string_view client_private_key_pem, bool dtls, rtc::KeyParams client_key_type = rtc::KeyParams(rtc::KT_DEFAULT), rtc::KeyParams server_key_type = rtc::KeyParams(rtc::KT_DEFAULT)) @@ -508,6 +510,49 @@ class SSLStreamAdapterTestBase : public ::testing::Test, } } + // This tests that we give up after 12 DTLS resends. + void TestHandshakeTimeout() { + rtc::ScopedFakeClock clock; + int64_t time_start = clock.TimeNanos(); + webrtc::TimeDelta time_increment = webrtc::TimeDelta::Millis(1000); + server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS); + client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS); + + if (!dtls_) { + // Make sure we simulate a reliable network for TLS. + // This is just a check to make sure that people don't write wrong + // tests. + RTC_CHECK_EQ(1460, mtu_); + RTC_CHECK(!loss_); + RTC_CHECK(!lose_first_packet_); + } + + if (!identities_set_) + SetPeerIdentitiesByDigest(true, true); + + // Start the handshake + int rv; + + server_ssl_->SetServerRole(); + rv = server_ssl_->StartSSL(); + ASSERT_EQ(0, rv); + + rv = client_ssl_->StartSSL(); + ASSERT_EQ(0, rv); + + // Now wait for the handshake to timeout (or fail after an hour of simulated + // time). + while (client_ssl_->GetState() == rtc::SS_OPENING && + (rtc::TimeDiff(clock.TimeNanos(), time_start) < + 3600 * rtc::kNumNanosecsPerSec)) { + EXPECT_TRUE_WAIT(!((client_ssl_->GetState() == rtc::SS_OPEN) && + (server_ssl_->GetState() == rtc::SS_OPEN)), + 1000); + clock.AdvanceTime(time_increment); + } + RTC_CHECK_EQ(client_ssl_->GetState(), rtc::SS_CLOSED); + } + // This tests that the handshake can complete before the identity is verified, // and the identity will be verified after the fact. It also verifies that // packets can't be read or written before the identity has been verified. @@ -683,7 +728,7 @@ class SSLStreamAdapterTestBase : public ::testing::Test, return server_ssl_->GetSslVersion(); } - bool ExportKeyingMaterial(const char* label, + bool ExportKeyingMaterial(absl::string_view label, const unsigned char* context, size_t context_len, bool use_context, @@ -864,8 +909,8 @@ class SSLStreamAdapterTestDTLSBase : public SSLStreamAdapterTestBase { count_(0), sent_(0) {} - SSLStreamAdapterTestDTLSBase(const std::string& cert_pem, - const std::string& private_key_pem) + SSLStreamAdapterTestDTLSBase(absl::string_view cert_pem, + absl::string_view private_key_pem) : SSLStreamAdapterTestBase(cert_pem, private_key_pem, true), client_buffer_(kBufferCapacity, kDefaultBufferSize), server_buffer_(kBufferCapacity, kDefaultBufferSize), @@ -983,8 +1028,8 @@ class SSLStreamAdapterTestDTLS : SSLStreamAdapterTestDTLSBase(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam())) {} - SSLStreamAdapterTestDTLS(const std::string& cert_pem, - const std::string& private_key_pem) + SSLStreamAdapterTestDTLS(absl::string_view cert_pem, + absl::string_view private_key_pem) : SSLStreamAdapterTestDTLSBase(cert_pem, private_key_pem) {} }; @@ -1215,6 +1260,12 @@ TEST_P(SSLStreamAdapterTestDTLS, DISABLED_TestDTLSConnectWithSmallMtu) { TestHandshake(); } +// Test a handshake with total loss and timing out. +TEST_P(SSLStreamAdapterTestDTLS, TestDTLSConnectTimeout) { + SetLoss(100); + TestHandshakeTimeout(); +} + // Test transfer -- trivial TEST_P(SSLStreamAdapterTestDTLS, TestDTLSTransfer) { TestHandshake(); @@ -1551,8 +1602,8 @@ class SSLStreamAdapterTestDTLSLegacyProtocols // initialized, so we set the experiment while creationg client_ssl_ // and server_ssl_. - void ConfigureClient(std::string experiment) { - webrtc::test::ScopedFieldTrials trial(experiment); + void ConfigureClient(absl::string_view experiment) { + webrtc::test::ScopedFieldTrials trial{std::string(experiment)}; client_stream_ = new SSLDummyStreamDTLS(this, "c2s", &client_buffer_, &server_buffer_); client_ssl_ = @@ -1564,8 +1615,8 @@ class SSLStreamAdapterTestDTLSLegacyProtocols client_ssl_->SetIdentity(std::move(client_identity)); } - void ConfigureServer(std::string experiment) { - webrtc::test::ScopedFieldTrials trial(experiment); + void ConfigureServer(absl::string_view experiment) { + webrtc::test::ScopedFieldTrials trial{std::string(experiment)}; server_stream_ = new SSLDummyStreamDTLS(this, "s2c", &server_buffer_, &client_buffer_); server_ssl_ = diff --git a/rtc_base/string_encode.cc b/rtc_base/string_encode.cc index 85fb992507..84361ae142 100644 --- a/rtc_base/string_encode.cc +++ b/rtc_base/string_encode.cc @@ -12,6 +12,8 @@ #include +#include "absl/strings/string_view.h" +#include "api/array_view.h" #include "rtc_base/arraysize.h" #include "rtc_base/checks.h" @@ -51,16 +53,16 @@ size_t hex_encode_output_length(size_t srclen, char delimiter) { // hex_encode shows the hex representation of binary data in ascii, with // `delimiter` between bytes, or none if `delimiter` == 0. void hex_encode_with_delimiter(char* buffer, - const char* csource, - size_t srclen, + absl::string_view source, char delimiter) { RTC_DCHECK(buffer); // Init and check bounds. const unsigned char* bsource = - reinterpret_cast(csource); + reinterpret_cast(source.data()); size_t srcpos = 0, bufpos = 0; + size_t srclen = source.length(); while (srcpos < srclen) { unsigned char ch = bsource[srcpos++]; buffer[bufpos] = hex_encode((ch >> 4) & 0xF); @@ -77,43 +79,30 @@ void hex_encode_with_delimiter(char* buffer, } // namespace -std::string hex_encode(const std::string& str) { - return hex_encode(str.c_str(), str.size()); +std::string hex_encode(absl::string_view str) { + return hex_encode_with_delimiter(str, 0); } -std::string hex_encode(const char* source, size_t srclen) { - return hex_encode_with_delimiter(source, srclen, 0); -} - -std::string hex_encode_with_delimiter(const char* source, - size_t srclen, +std::string hex_encode_with_delimiter(absl::string_view source, char delimiter) { - std::string s(hex_encode_output_length(srclen, delimiter), 0); - hex_encode_with_delimiter(&s[0], source, srclen, delimiter); + std::string s(hex_encode_output_length(source.length(), delimiter), 0); + hex_encode_with_delimiter(&s[0], source, delimiter); return s; } -size_t hex_decode(char* cbuffer, - size_t buflen, - const char* source, - size_t srclen) { - return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0); -} - -size_t hex_decode_with_delimiter(char* cbuffer, - size_t buflen, - const char* source, - size_t srclen, +size_t hex_decode_with_delimiter(ArrayView cbuffer, + absl::string_view source, char delimiter) { - RTC_DCHECK(cbuffer); // TODO(kwiberg): estimate output size - if (buflen == 0) + if (cbuffer.empty()) return 0; // Init and bounds check. - unsigned char* bbuffer = reinterpret_cast(cbuffer); + unsigned char* bbuffer = reinterpret_cast(cbuffer.data()); size_t srcpos = 0, bufpos = 0; + size_t srclen = source.length(); + size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2; - if (buflen < needed) + if (cbuffer.size() < needed) return 0; while (srcpos < srclen) { @@ -141,15 +130,8 @@ size_t hex_decode_with_delimiter(char* cbuffer, return bufpos; } -size_t hex_decode(char* buffer, size_t buflen, const std::string& source) { - return hex_decode_with_delimiter(buffer, buflen, source, 0); -} -size_t hex_decode_with_delimiter(char* buffer, - size_t buflen, - const std::string& source, - char delimiter) { - return hex_decode_with_delimiter(buffer, buflen, source.c_str(), - source.length(), delimiter); +size_t hex_decode(ArrayView buffer, absl::string_view source) { + return hex_decode_with_delimiter(buffer, source, 0); } size_t tokenize(absl::string_view source, @@ -177,7 +159,7 @@ bool tokenize_first(absl::string_view source, std::string* rest) { // Find the first delimiter size_t left_pos = source.find(delimiter); - if (left_pos == std::string::npos) { + if (left_pos == absl::string_view::npos) { return false; } @@ -192,28 +174,6 @@ bool tokenize_first(absl::string_view source, return true; } -std::string join(const std::vector& source, char delimiter) { - if (source.size() == 0) { - return std::string(); - } - // Find length of the string to be returned to pre-allocate memory. - size_t source_string_length = 0; - for (size_t i = 0; i < source.size(); ++i) { - source_string_length += source[i].length(); - } - - // Build the joined string. - std::string joined_string; - joined_string.reserve(source_string_length + source.size() - 1); - for (size_t i = 0; i < source.size(); ++i) { - if (i != 0) { - joined_string += delimiter; - } - joined_string += source[i]; - } - return joined_string; -} - std::vector split(absl::string_view source, char delimiter) { std::vector fields; size_t last = 0; @@ -242,11 +202,12 @@ std::string ToString(const bool b) { return b ? "true" : "false"; } -std::string ToString(const char* const s) { +std::string ToString(absl::string_view s) { return std::string(s); } -std::string ToString(const std::string s) { - return s; + +std::string ToString(const char* s) { + return std::string(s); } std::string ToString(const short s) { @@ -319,7 +280,7 @@ std::string ToString(const void* const p) { return std::string(&buf[0], len); } -bool FromString(const std::string& s, bool* b) { +bool FromString(absl::string_view s, bool* b) { if (s == "false") { *b = false; return true; diff --git a/rtc_base/string_encode.h b/rtc_base/string_encode.h index c63d5271fa..6bbb0088f3 100644 --- a/rtc_base/string_encode.h +++ b/rtc_base/string_encode.h @@ -19,6 +19,7 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "api/array_view.h" #include "rtc_base/checks.h" #include "rtc_base/string_to_number.h" @@ -28,39 +29,20 @@ namespace rtc { // String Encoding Utilities ////////////////////////////////////////////////////////////////////// -std::string hex_encode(const std::string& str); -std::string hex_encode(const char* source, size_t srclen); -std::string hex_encode_with_delimiter(const char* source, - size_t srclen, - char delimiter); +std::string hex_encode(absl::string_view str); +std::string hex_encode_with_delimiter(absl::string_view source, char delimiter); // hex_decode converts ascii hex to binary. -size_t hex_decode(char* buffer, - size_t buflen, - const char* source, - size_t srclen); +size_t hex_decode(ArrayView buffer, absl::string_view source); // hex_decode, assuming that there is a delimiter between every byte // pair. // `delimiter` == 0 means no delimiter // If the buffer is too short or the data is invalid, we return 0. -size_t hex_decode_with_delimiter(char* buffer, - size_t buflen, - const char* source, - size_t srclen, +size_t hex_decode_with_delimiter(ArrayView buffer, + absl::string_view source, char delimiter); -// Helper functions for hex_decode. -size_t hex_decode(char* buffer, size_t buflen, const std::string& source); -size_t hex_decode_with_delimiter(char* buffer, - size_t buflen, - const std::string& source, - char delimiter); - -// Joins the source vector of strings into a single string, with each -// field in source being separated by delimiter. No trailing delimiter is added. -std::string join(const std::vector& source, char delimiter); - // Splits the source string into multiple fields separated by delimiter, // with duplicates of delimiter creating empty fields. Empty input produces a // single, empty, field. @@ -88,8 +70,10 @@ bool tokenize_first(absl::string_view source, // TODO(jonasolsson): Remove these when absl::StrCat becomes available. std::string ToString(bool b); +std::string ToString(absl::string_view s); +// The const char* overload is needed for correct overload resolution because of +// the const void* version of ToString() below. std::string ToString(const char* s); -std::string ToString(std::string t); std::string ToString(short s); std::string ToString(unsigned short s); @@ -109,7 +93,7 @@ template ::value && !std::is_same::value, int>::type = 0> -static bool FromString(const std::string& s, T* t) { +static bool FromString(absl::string_view s, T* t) { RTC_DCHECK(t); absl::optional result = StringToNumber(s); @@ -119,10 +103,10 @@ static bool FromString(const std::string& s, T* t) { return result.has_value(); } -bool FromString(const std::string& s, bool* b); +bool FromString(absl::string_view s, bool* b); template -static inline T FromString(const std::string& str) { +static inline T FromString(absl::string_view str) { T val; FromString(str, &val); return val; diff --git a/rtc_base/string_encode_unittest.cc b/rtc_base/string_encode_unittest.cc index 3dd79c954f..4afed3f00f 100644 --- a/rtc_base/string_encode_unittest.cc +++ b/rtc_base/string_encode_unittest.cc @@ -14,6 +14,7 @@ #include // no-presubmit-check TODO(webrtc:8982) +#include "api/array_view.h" #include "test/gtest.h" namespace rtc { @@ -28,109 +29,106 @@ class HexEncodeTest : public ::testing::Test { } char data_[10]; + absl::string_view data_view_{data_, sizeof(data_)}; char decoded_[11]; size_t dec_res_; }; // Test that we can convert to/from hex with no delimiter. TEST_F(HexEncodeTest, TestWithNoDelimiter) { - std::string encoded = hex_encode(data_, sizeof(data_)); + std::string encoded = hex_encode(data_view_); EXPECT_EQ("80818283848586878889", encoded); - dec_res_ = - hex_decode(decoded_, sizeof(decoded_), encoded.data(), encoded.size()); + dec_res_ = hex_decode(ArrayView(decoded_), encoded); ASSERT_EQ(sizeof(data_), dec_res_); ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); } // Test that we can convert to/from hex with a colon delimiter. TEST_F(HexEncodeTest, TestWithDelimiter) { - std::string encoded = hex_encode_with_delimiter(data_, sizeof(data_), ':'); + std::string encoded = hex_encode_with_delimiter(data_view_, ':'); EXPECT_EQ("80:81:82:83:84:85:86:87:88:89", encoded); - dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), - encoded.data(), encoded.size(), ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); ASSERT_EQ(sizeof(data_), dec_res_); ASSERT_EQ(0, memcmp(data_, decoded_, dec_res_)); } // Test that encoding with one delimiter and decoding with another fails. TEST_F(HexEncodeTest, TestWithWrongDelimiter) { - std::string encoded = hex_encode_with_delimiter(data_, sizeof(data_), ':'); - dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), - encoded.data(), encoded.size(), '/'); + std::string encoded = hex_encode_with_delimiter(data_view_, ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, '/'); ASSERT_EQ(0U, dec_res_); } // Test that encoding without a delimiter and decoding with one fails. TEST_F(HexEncodeTest, TestExpectedDelimiter) { - std::string encoded = hex_encode(data_, sizeof(data_)); + std::string encoded = hex_encode(data_view_); EXPECT_EQ(sizeof(data_) * 2, encoded.size()); - dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), - encoded.data(), encoded.size(), ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); ASSERT_EQ(0U, dec_res_); } // Test that encoding with a delimiter and decoding without one fails. TEST_F(HexEncodeTest, TestExpectedNoDelimiter) { - std::string encoded = hex_encode_with_delimiter(data_, sizeof(data_), ':'); + std::string encoded = hex_encode_with_delimiter(data_view_, ':'); EXPECT_EQ(sizeof(data_) * 3 - 1, encoded.size()); - dec_res_ = - hex_decode(decoded_, sizeof(decoded_), encoded.data(), encoded.size()); + dec_res_ = hex_decode(ArrayView(decoded_), encoded); ASSERT_EQ(0U, dec_res_); } // Test that we handle a zero-length buffer with no delimiter. TEST_F(HexEncodeTest, TestZeroLengthNoDelimiter) { - std::string encoded = hex_encode("", 0); + std::string encoded = hex_encode(""); EXPECT_TRUE(encoded.empty()); - dec_res_ = - hex_decode(decoded_, sizeof(decoded_), encoded.data(), encoded.size()); + dec_res_ = hex_decode(ArrayView(decoded_), encoded); ASSERT_EQ(0U, dec_res_); } // Test that we handle a zero-length buffer with a delimiter. TEST_F(HexEncodeTest, TestZeroLengthWithDelimiter) { - std::string encoded = hex_encode_with_delimiter("", 0, ':'); + std::string encoded = hex_encode_with_delimiter("", ':'); EXPECT_TRUE(encoded.empty()); - dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), - encoded.data(), encoded.size(), ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), encoded, ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding into a too-small output buffer fails. TEST_F(HexEncodeTest, TestDecodeTooShort) { - dec_res_ = hex_decode_with_delimiter(decoded_, 4, "0123456789", 10, 0); + dec_res_ = + hex_decode_with_delimiter(ArrayView(decoded_, 4), "0123456789", 0); ASSERT_EQ(0U, dec_res_); ASSERT_EQ(0x7f, decoded_[4]); } // Test that decoding non-hex data fails. TEST_F(HexEncodeTest, TestDecodeBogusData) { - dec_res_ = - hex_decode_with_delimiter(decoded_, sizeof(decoded_), "axyz", 4, 0); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), "axyz", 0); ASSERT_EQ(0U, dec_res_); } // Test that decoding an odd number of hex characters fails. TEST_F(HexEncodeTest, TestDecodeOddHexDigits) { - dec_res_ = hex_decode_with_delimiter(decoded_, sizeof(decoded_), "012", 3, 0); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_), "012", 0); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with too many delimiters fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterTooManyDelimiters) { - dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01::23::45::67", 14, ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + "01::23::45::67", ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with a leading delimiter fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterLeadingDelimiter) { - dec_res_ = hex_decode_with_delimiter(decoded_, 4, ":01:23:45:67", 12, ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + ":01:23:45:67", ':'); ASSERT_EQ(0U, dec_res_); } // Test that decoding a string with a trailing delimiter fails. TEST_F(HexEncodeTest, TestDecodeWithDelimiterTrailingDelimiter) { - dec_res_ = hex_decode_with_delimiter(decoded_, 4, "01:23:45:67:", 12, ':'); + dec_res_ = hex_decode_with_delimiter(ArrayView(decoded_, 4), + "01:23:45:67:", ':'); ASSERT_EQ(0U, dec_res_); } diff --git a/rtc_base/string_to_number.cc b/rtc_base/string_to_number.cc index 351610f31a..ab3f1abb8e 100644 --- a/rtc_base/string_to_number.cc +++ b/rtc_base/string_to_number.cc @@ -20,30 +20,41 @@ namespace rtc { namespace string_to_number_internal { -absl::optional ParseSigned(const char* str, int base) { - RTC_DCHECK(str); +absl::optional ParseSigned(absl::string_view str, int base) { + if (str.empty()) + return absl::nullopt; + if (isdigit(str[0]) || str[0] == '-') { + std::string str_str = std::string(str); char* end = nullptr; errno = 0; - const signed_type value = std::strtoll(str, &end, base); - if (end && *end == '\0' && errno == 0) { + const signed_type value = std::strtoll(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0) { return value; } } return absl::nullopt; } -absl::optional ParseUnsigned(const char* str, int base) { - RTC_DCHECK(str); +absl::optional ParseUnsigned(absl::string_view str, int base) { + if (str.empty()) + return absl::nullopt; + if (isdigit(str[0]) || str[0] == '-') { + std::string str_str = std::string(str); // Explicitly discard negative values. std::strtoull parsing causes unsigned // wraparound. We cannot just reject values that start with -, though, since // -0 is perfectly fine, as is -0000000000000000000000000000000. const bool is_negative = str[0] == '-'; char* end = nullptr; errno = 0; - const unsigned_type value = std::strtoull(str, &end, base); - if (end && *end == '\0' && errno == 0 && (value == 0 || !is_negative)) { + const unsigned_type value = std::strtoull(str_str.c_str(), &end, base); + // Check for errors and also make sure that there were no embedded nuls in + // the input string. + if (end == str_str.c_str() + str_str.size() && errno == 0 && + (value == 0 || !is_negative)) { return value; } } @@ -69,22 +80,25 @@ inline long double StrToT(const char* str, char** str_end) { } template -absl::optional ParseFloatingPoint(const char* str) { - RTC_DCHECK(str); - if (*str == '\0') +absl::optional ParseFloatingPoint(absl::string_view str) { + if (str.empty()) + return absl::nullopt; + + if (str[0] == '\0') return absl::nullopt; + std::string str_str = std::string(str); char* end = nullptr; errno = 0; - const T value = StrToT(str, &end); - if (end && *end == '\0' && errno == 0) { + const T value = StrToT(str_str.c_str(), &end); + if (end == str_str.c_str() + str_str.size() && errno == 0) { return value; } return absl::nullopt; } -template absl::optional ParseFloatingPoint(const char* str); -template absl::optional ParseFloatingPoint(const char* str); -template absl::optional ParseFloatingPoint(const char* str); +template absl::optional ParseFloatingPoint(absl::string_view str); +template absl::optional ParseFloatingPoint(absl::string_view str); +template absl::optional ParseFloatingPoint(absl::string_view str); } // namespace string_to_number_internal } // namespace rtc diff --git a/rtc_base/string_to_number.h b/rtc_base/string_to_number.h index 4cb521595d..1d704ee464 100644 --- a/rtc_base/string_to_number.h +++ b/rtc_base/string_to_number.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "absl/types/optional.h" namespace rtc { @@ -25,10 +26,9 @@ namespace rtc { // functions (std::stoi, etc.) indicate errors by throwing exceptions, which // are disabled in WebRTC. // -// Integers are parsed using one of the following functions: -// absl::optional StringToNumber(const char* str, int base = 10); -// absl::optional StringToNumber(const std::string& str, -// int base = 10); +// Integers are parsed using: +// absl::optional StringToNumber(absl::string_view str, +// int base = 10); // // These functions parse a value from the beginning of a string into one of the // fundamental integer types, or returns an empty Optional if parsing @@ -38,26 +38,23 @@ namespace rtc { // By setting base to 0, one of octal, decimal or hexadecimal will be // detected from the string's prefix (0, nothing or 0x, respectively). // If non-zero, base can be set to a value between 2 and 36 inclusively. -// -// If desired, this interface could be extended with support for floating-point -// types. namespace string_to_number_internal { // These must be (unsigned) long long, to match the signature of strto(u)ll. using unsigned_type = unsigned long long; // NOLINT(runtime/int) using signed_type = long long; // NOLINT(runtime/int) -absl::optional ParseSigned(const char* str, int base); -absl::optional ParseUnsigned(const char* str, int base); +absl::optional ParseSigned(absl::string_view str, int base); +absl::optional ParseUnsigned(absl::string_view str, int base); template -absl::optional ParseFloatingPoint(const char* str); +absl::optional ParseFloatingPoint(absl::string_view str); } // namespace string_to_number_internal template typename std::enable_if::value && std::is_signed::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { using string_to_number_internal::signed_type; static_assert( std::numeric_limits::max() <= @@ -78,7 +75,7 @@ template typename std::enable_if::value && std::is_unsigned::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { using string_to_number_internal::unsigned_type; static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), @@ -95,7 +92,7 @@ StringToNumber(const char* str, int base = 10) { template typename std::enable_if::value, absl::optional>::type -StringToNumber(const char* str, int base = 10) { +StringToNumber(absl::string_view str, int base = 10) { static_assert( std::numeric_limits::max() <= std::numeric_limits::max(), "StringToNumber only supports floating-point numbers as large " @@ -103,14 +100,6 @@ StringToNumber(const char* str, int base = 10) { return string_to_number_internal::ParseFloatingPoint(str); } -// The std::string overloads only exists if there is a matching const char* -// version. -template -auto StringToNumber(const std::string& str, int base = 10) - -> decltype(StringToNumber(str.c_str(), base)) { - return StringToNumber(str.c_str(), base); -} - } // namespace rtc #endif // RTC_BASE_STRING_TO_NUMBER_H_ diff --git a/rtc_base/string_to_number_unittest.cc b/rtc_base/string_to_number_unittest.cc index f460b15e58..edfdbf69ff 100644 --- a/rtc_base/string_to_number_unittest.cc +++ b/rtc_base/string_to_number_unittest.cc @@ -15,6 +15,8 @@ #include #include +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "test/gtest.h" namespace rtc { @@ -80,6 +82,10 @@ TYPED_TEST_P(BasicNumberTest, TestInvalidInputs) { const char kInvalidCharArray[] = "Invalid string containing 47"; const char kPlusMinusCharArray[] = "+-100"; const char kNumberFollowedByCruft[] = "640x480"; + const char kEmbeddedNul[] = {'1', '2', '\0', '3', '4'}; + const char kBeginningEmbeddedNul[] = {'\0', '1', '2', '3', '4'}; + const char kTrailingEmbeddedNul[] = {'1', '2', '3', '4', '\0'}; + EXPECT_EQ(absl::nullopt, StringToNumber(kInvalidCharArray)); EXPECT_EQ(absl::nullopt, StringToNumber(std::string(kInvalidCharArray))); EXPECT_EQ(absl::nullopt, StringToNumber(kPlusMinusCharArray)); @@ -92,6 +98,23 @@ TYPED_TEST_P(BasicNumberTest, TestInvalidInputs) { EXPECT_EQ(absl::nullopt, StringToNumber("- 5")); EXPECT_EQ(absl::nullopt, StringToNumber(" -5")); EXPECT_EQ(absl::nullopt, StringToNumber("5 ")); + // Test various types of empty inputs + EXPECT_EQ(absl::nullopt, StringToNumber({nullptr, 0})); + EXPECT_EQ(absl::nullopt, StringToNumber("")); + EXPECT_EQ(absl::nullopt, StringToNumber(std::string())); + EXPECT_EQ(absl::nullopt, StringToNumber(std::string(""))); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view())); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view(nullptr, 0))); + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view(""))); + // Test strings with embedded nuls. + EXPECT_EQ(absl::nullopt, StringToNumber(absl::string_view( + kEmbeddedNul, sizeof(kEmbeddedNul)))); + EXPECT_EQ(absl::nullopt, + StringToNumber(absl::string_view( + kBeginningEmbeddedNul, sizeof(kBeginningEmbeddedNul)))); + EXPECT_EQ(absl::nullopt, + StringToNumber(absl::string_view(kTrailingEmbeddedNul, + sizeof(kTrailingEmbeddedNul)))); } REGISTER_TYPED_TEST_SUITE_P(BasicNumberTest, diff --git a/rtc_base/string_utils.cc b/rtc_base/string_utils.cc index 1720c62d5e..b93e615705 100644 --- a/rtc_base/string_utils.cc +++ b/rtc_base/string_utils.cc @@ -10,39 +10,23 @@ #include "rtc_base/string_utils.h" +#include "absl/strings/string_view.h" + namespace rtc { -size_t strcpyn(char* buffer, - size_t buflen, - const char* source, - size_t srclen /* = SIZE_UNKNOWN */) { +size_t strcpyn(char* buffer, size_t buflen, absl::string_view source) { if (buflen <= 0) return 0; - if (srclen == SIZE_UNKNOWN) { - srclen = strlen(source); - } + size_t srclen = source.length(); if (srclen >= buflen) { srclen = buflen - 1; } - memcpy(buffer, source, srclen); + memcpy(buffer, source.data(), srclen); buffer[srclen] = 0; return srclen; } -static const char kWhitespace[] = " \n\r\t"; - -std::string string_trim(const std::string& s) { - std::string::size_type first = s.find_first_not_of(kWhitespace); - std::string::size_type last = s.find_last_not_of(kWhitespace); - - if (first == std::string::npos || last == std::string::npos) { - return std::string(""); - } - - return s.substr(first, last - first + 1); -} - std::string ToHex(const int i) { char buffer[50]; snprintf(buffer, sizeof(buffer), "%x", i); diff --git a/rtc_base/string_utils.h b/rtc_base/string_utils.h index 6e8b0b599b..d062675a94 100644 --- a/rtc_base/string_utils.h +++ b/rtc_base/string_utils.h @@ -16,6 +16,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #include @@ -30,15 +32,26 @@ #include +#include "absl/strings/string_view.h" + namespace rtc { const size_t SIZE_UNKNOWN = static_cast(-1); +// An absl::string_view comparator functor for use with container types such as +// std::map that support heterogenous lookup. +// +// Example usage: +// std::map my_map; +struct AbslStringViewCmp { + using is_transparent = void; + bool operator()(absl::string_view a, absl::string_view b) const { + return a < b; + } +}; + // Safe version of strncpy that always nul-terminate. -size_t strcpyn(char* buffer, - size_t buflen, - const char* source, - size_t srclen = SIZE_UNKNOWN); +size_t strcpyn(char* buffer, size_t buflen, absl::string_view source); /////////////////////////////////////////////////////////////////////////////// // UTF helpers (Windows only) @@ -57,7 +70,7 @@ inline std::wstring ToUtf16(const char* utf8, size_t len) { return ws; } -inline std::wstring ToUtf16(const std::string& str) { +inline std::wstring ToUtf16(absl::string_view str) { return ToUtf16(str.data(), str.length()); } @@ -82,9 +95,6 @@ inline std::string ToUtf8(const std::wstring& wstr) { #endif // WEBRTC_WIN -// Remove leading and trailing whitespaces. -std::string string_trim(const std::string& s); - // TODO(jonasolsson): replace with absl::Hex when that becomes available. std::string ToHex(int i); diff --git a/rtc_base/string_utils_unittest.cc b/rtc_base/string_utils_unittest.cc index 120f7e60f5..4e4bebdda7 100644 --- a/rtc_base/string_utils_unittest.cc +++ b/rtc_base/string_utils_unittest.cc @@ -14,14 +14,6 @@ namespace rtc { -TEST(string_trim_Test, Trimming) { - EXPECT_EQ("temp", string_trim("\n\r\t temp \n\r\t")); - EXPECT_EQ("temp\n\r\t temp", string_trim(" temp\n\r\t temp ")); - EXPECT_EQ("temp temp", string_trim("temp temp")); - EXPECT_EQ("", string_trim(" \r\n\t")); - EXPECT_EQ("", string_trim("")); -} - TEST(string_toHexTest, ToHex) { EXPECT_EQ(ToHex(0), "0"); EXPECT_EQ(ToHex(0X1243E), "1243e"); diff --git a/rtc_base/strings/json.cc b/rtc_base/strings/json.cc index 99664404cf..af8ba184e7 100644 --- a/rtc_base/strings/json.cc +++ b/rtc_base/strings/json.cc @@ -14,6 +14,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/string_encode.h" namespace rtc { @@ -240,46 +241,47 @@ bool GetDoubleFromJsonArray(const Json::Value& in, size_t n, double* out) { } bool GetValueFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, Json::Value* out) { - if (!in.isObject() || !in.isMember(k)) { + std::string k_str = std::string(k); + if (!in.isObject() || !in.isMember(k_str)) { return false; } - *out = in[k]; + *out = in[k_str]; return true; } bool GetIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, int* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetIntFromJson(x, out); } bool GetUIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, unsigned int* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetUIntFromJson(x, out); } bool GetStringFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, std::string* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetStringFromJson(x, out); } bool GetBoolFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, bool* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetBoolFromJson(x, out); } bool GetDoubleFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, double* out) { Json::Value x; return GetValueFromJsonObject(in, k, &x) && GetDoubleFromJson(x, out); diff --git a/rtc_base/strings/json.h b/rtc_base/strings/json.h index 0cb9542c7f..618cb71b04 100644 --- a/rtc_base/strings/json.h +++ b/rtc_base/strings/json.h @@ -14,6 +14,8 @@ #include #include +#include "absl/strings/string_view.h" + #if !defined(WEBRTC_EXTERNAL_JSON) #include "json/json.h" #else @@ -62,22 +64,20 @@ Json::Value DoubleVectorToJsonArray(const std::vector& in); // Pull values out of a JSON object. bool GetValueFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, Json::Value* out); -bool GetIntFromJsonObject(const Json::Value& in, - const std::string& k, - int* out); +bool GetIntFromJsonObject(const Json::Value& in, absl::string_view k, int* out); bool GetUIntFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, unsigned int* out); bool GetStringFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, std::string* out); bool GetBoolFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, bool* out); bool GetDoubleFromJsonObject(const Json::Value& in, - const std::string& k, + absl::string_view k, double* out); // Writes out a Json value as a string. diff --git a/rtc_base/strings/string_builder.cc b/rtc_base/strings/string_builder.cc index 7536cd77dd..a419b0b3cc 100644 --- a/rtc_base/strings/string_builder.cc +++ b/rtc_base/strings/string_builder.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" #include "rtc_base/numerics/safe_minmax.h" @@ -26,16 +27,20 @@ SimpleStringBuilder::SimpleStringBuilder(rtc::ArrayView buffer) RTC_DCHECK(IsConsistent()); } -SimpleStringBuilder& SimpleStringBuilder::operator<<(const char* str) { - return Append(str, strlen(str)); -} - SimpleStringBuilder& SimpleStringBuilder::operator<<(char ch) { - return Append(&ch, 1); + return operator<<(absl::string_view(&ch, 1)); } -SimpleStringBuilder& SimpleStringBuilder::operator<<(const std::string& str) { - return Append(str.c_str(), str.length()); +SimpleStringBuilder& SimpleStringBuilder::operator<<(absl::string_view str) { + RTC_DCHECK_LT(size_ + str.length(), buffer_.size()) + << "Buffer size was insufficient"; + const size_t chars_added = + rtc::SafeMin(str.length(), buffer_.size() - size_ - 1); + memcpy(&buffer_[size_], str.data(), chars_added); + size_ += chars_added; + buffer_[size_] = '\0'; + RTC_DCHECK(IsConsistent()); + return *this; } // Numeric conversion routines. @@ -106,18 +111,6 @@ SimpleStringBuilder& SimpleStringBuilder::AppendFormat(const char* fmt, ...) { return *this; } -SimpleStringBuilder& SimpleStringBuilder::Append(const char* str, - size_t length) { - RTC_DCHECK_LT(size_ + length, buffer_.size()) - << "Buffer size was insufficient"; - const size_t chars_added = rtc::SafeMin(length, buffer_.size() - size_ - 1); - memcpy(&buffer_[size_], str, chars_added); - size_ += chars_added; - buffer_[size_] = '\0'; - RTC_DCHECK(IsConsistent()); - return *this; -} - StringBuilder& StringBuilder::AppendFormat(const char* fmt, ...) { va_list args, copy; va_start(args, fmt); diff --git a/rtc_base/strings/string_builder.h b/rtc_base/strings/string_builder.h index 6fe478ce4c..00986371d3 100644 --- a/rtc_base/strings/string_builder.h +++ b/rtc_base/strings/string_builder.h @@ -32,9 +32,8 @@ class SimpleStringBuilder { SimpleStringBuilder(const SimpleStringBuilder&) = delete; SimpleStringBuilder& operator=(const SimpleStringBuilder&) = delete; - SimpleStringBuilder& operator<<(const char* str); SimpleStringBuilder& operator<<(char ch); - SimpleStringBuilder& operator<<(const std::string& str); + SimpleStringBuilder& operator<<(absl::string_view str); SimpleStringBuilder& operator<<(int i); SimpleStringBuilder& operator<<(unsigned i); SimpleStringBuilder& operator<<(long i); // NOLINT @@ -61,10 +60,6 @@ class SimpleStringBuilder { SimpleStringBuilder& AppendFormat(const char* fmt, ...); - // An alternate way from operator<<() to append a string. This variant is - // slightly more efficient when the length of the string to append, is known. - SimpleStringBuilder& Append(const char* str, size_t length); - private: bool IsConsistent() const { return size_ <= buffer_.size() - 1 && buffer_[size_] == '\0'; diff --git a/rtc_base/strings/string_format_unittest.cc b/rtc_base/strings/string_format_unittest.cc index d0e8eb2d71..5531001979 100644 --- a/rtc_base/strings/string_format_unittest.cc +++ b/rtc_base/strings/string_format_unittest.cc @@ -10,7 +10,11 @@ #include "rtc_base/strings/string_format.h" +#include + +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" +#include "rtc_base/string_encode.h" #include "test/gtest.h" namespace rtc { @@ -32,4 +36,17 @@ TEST(StringFormatTest, MaxSizeShouldWork) { EXPECT_EQ(str, StringFormat("%s", str)); } +// Test that formating a string using `absl::string_view` works as expected +// whe using `%.*s`. +TEST(StringFormatTest, FormatStringView) { + const std::string main_string("This is a substring test."); + std::vector string_views = rtc::split(main_string, ' '); + ASSERT_EQ(string_views.size(), 5u); + + const absl::string_view& sv = string_views[3]; + std::string formatted = + StringFormat("We have a %.*s.", static_cast(sv.size()), sv.data()); + EXPECT_EQ(formatted.compare("We have a substring."), 0); +} + } // namespace rtc diff --git a/rtc_base/synchronization/BUILD.gn b/rtc_base/synchronization/BUILD.gn index 9d891f3d8e..5890e75e46 100644 --- a/rtc_base/synchronization/BUILD.gn +++ b/rtc_base/synchronization/BUILD.gn @@ -21,9 +21,8 @@ rtc_library("yield") { deps = [] } -rtc_library("mutex") { +rtc_source_set("mutex") { sources = [ - "mutex.cc", "mutex.h", "mutex_critical_section.h", "mutex_pthread.h", @@ -87,7 +86,9 @@ if (rtc_include_tests) { ":yield", ":yield_policy", "..:checks", + "..:location", "..:macromagic", + "..:platform_thread", "..:rtc_base", "..:rtc_event", "..:threading", diff --git a/rtc_base/synchronization/mutex.cc b/rtc_base/synchronization/mutex.cc deleted file mode 100644 index 6c2d6ff7f0..0000000000 --- a/rtc_base/synchronization/mutex.cc +++ /dev/null @@ -1,39 +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. - */ - -#include "rtc_base/synchronization/mutex.h" - -#include "rtc_base/checks.h" -#include "rtc_base/synchronization/yield.h" - -namespace webrtc { - -#if !defined(WEBRTC_ABSL_MUTEX) -void GlobalMutex::Lock() { - while (mutex_locked_.exchange(1)) { - YieldCurrentThread(); - } -} - -void GlobalMutex::Unlock() { - int old = mutex_locked_.exchange(0); - RTC_DCHECK_EQ(old, 1) << "Unlock called without calling Lock first"; -} - -GlobalMutexLock::GlobalMutexLock(GlobalMutex* mutex) : mutex_(mutex) { - mutex_->Lock(); -} - -GlobalMutexLock::~GlobalMutexLock() { - mutex_->Unlock(); -} -#endif // #if !defined(WEBRTC_ABSL_MUTEX) - -} // namespace webrtc diff --git a/rtc_base/synchronization/mutex.h b/rtc_base/synchronization/mutex.h index c6af9e9838..2cf0e67c3d 100644 --- a/rtc_base/synchronization/mutex.h +++ b/rtc_base/synchronization/mutex.h @@ -72,41 +72,6 @@ class RTC_SCOPED_LOCKABLE MutexLock final { Mutex* mutex_; }; -// A mutex used to protect global variables. Do NOT use for other purposes. -#if defined(WEBRTC_ABSL_MUTEX) -using GlobalMutex = absl::Mutex; -using GlobalMutexLock = absl::MutexLock; -#else -class RTC_LOCKABLE GlobalMutex final { - public: - GlobalMutex(const GlobalMutex&) = delete; - GlobalMutex& operator=(const GlobalMutex&) = delete; - - constexpr explicit GlobalMutex(absl::ConstInitType /*unused*/) - : mutex_locked_(0) {} - - void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION(); - void Unlock() RTC_UNLOCK_FUNCTION(); - - private: - std::atomic mutex_locked_; // 0 means lock not taken, 1 means taken. -}; - -// GlobalMutexLock, for serializing execution through a scope. -class RTC_SCOPED_LOCKABLE GlobalMutexLock final { - public: - GlobalMutexLock(const GlobalMutexLock&) = delete; - GlobalMutexLock& operator=(const GlobalMutexLock&) = delete; - - explicit GlobalMutexLock(GlobalMutex* mutex) - RTC_EXCLUSIVE_LOCK_FUNCTION(mutex_); - ~GlobalMutexLock() RTC_UNLOCK_FUNCTION(); - - private: - GlobalMutex* mutex_; -}; -#endif // if defined(WEBRTC_ABSL_MUTEX) - } // namespace webrtc #endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_ diff --git a/rtc_base/synchronization/mutex_unittest.cc b/rtc_base/synchronization/mutex_unittest.cc index a07e8fa429..fcca78d18b 100644 --- a/rtc_base/synchronization/mutex_unittest.cc +++ b/rtc_base/synchronization/mutex_unittest.cc @@ -177,30 +177,5 @@ TEST(MutexTest, ProtectsSharedResourceWithMutexAndMutexLocker) { EXPECT_EQ(0, runner.shared_value()); } -TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndRawMutexLocker) { - std::vector> threads; - LockRunner> runner(absl::kConstInit); - StartThreads(threads, &runner); - runner.SetExpectedThreadCount(kNumThreads); - EXPECT_TRUE(runner.Run()); - EXPECT_EQ(0, runner.shared_value()); -} - -TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndMutexLocker) { - std::vector> threads; - LockRunner> runner( - absl::kConstInit); - StartThreads(threads, &runner); - runner.SetExpectedThreadCount(kNumThreads); - EXPECT_TRUE(runner.Run()); - EXPECT_EQ(0, runner.shared_value()); -} - -TEST(MutexTest, GlobalMutexCanHaveStaticStorageDuration) { - ABSL_CONST_INIT static GlobalMutex global_lock(absl::kConstInit); - global_lock.Lock(); - global_lock.Unlock(); -} - } // namespace } // namespace webrtc diff --git a/rtc_base/system/BUILD.gn b/rtc_base/system/BUILD.gn index c604796e60..a34a151856 100644 --- a/rtc_base/system/BUILD.gn +++ b/rtc_base/system/BUILD.gn @@ -30,6 +30,7 @@ rtc_library("file_wrapper") { "..:criticalsection", "..:safe_conversions", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } if (rtc_include_tests) { @@ -91,19 +92,6 @@ if (is_mac || is_ios) { } } -rtc_source_set("thread_registry") { - sources = [ "thread_registry.h" ] - deps = [ - "..:rtc_base_approved", - "../synchronization:mutex", - ] - if (is_android && !build_with_chromium) { - sources += [ "thread_registry.cc" ] - deps += [ "../../sdk/android:native_api_stacktrace" ] - absl_deps = [ "//third_party/abseil-cpp/absl/base:core_headers" ] - } -} - rtc_source_set("warn_current_thread_is_deadlocked") { sources = [ "warn_current_thread_is_deadlocked.h" ] deps = [] diff --git a/rtc_base/system/DEPS b/rtc_base/system/DEPS index 39293d02b5..ab9449f70a 100644 --- a/rtc_base/system/DEPS +++ b/rtc_base/system/DEPS @@ -1,7 +1,4 @@ specific_include_rules = { - "thread_registry\.cc": [ - "+sdk/android/native_api/stacktrace/stacktrace.h", - ], "warn_current_thread_is_deadlocked\.cc": [ "+sdk/android/native_api/stacktrace/stacktrace.h", ], diff --git a/rtc_base/system/file_wrapper.cc b/rtc_base/system/file_wrapper.cc index 3e49315793..1979e6f5e5 100644 --- a/rtc_base/system/file_wrapper.cc +++ b/rtc_base/system/file_wrapper.cc @@ -9,10 +9,12 @@ */ #include "rtc_base/system/file_wrapper.h" -#include "rtc_base/numerics/safe_conversions.h" #include +#include "absl/strings/string_view.h" +#include "rtc_base/numerics/safe_conversions.h" + #ifdef _WIN32 #include #else @@ -23,14 +25,17 @@ namespace webrtc { namespace { -FILE* FileOpen(const char* file_name_utf8, bool read_only, int* error) { +FILE* FileOpen(absl::string_view file_name_utf8, bool read_only, int* error) { + RTC_CHECK_EQ(file_name_utf8.find_first_of('\0'), absl::string_view::npos) + << "Invalid filename, containing NUL character"; + std::string file_name = std::string(file_name_utf8); #if defined(_WIN32) - int len = MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, nullptr, 0); + int len = MultiByteToWideChar(CP_UTF8, 0, file_name.c_str(), -1, nullptr, 0); std::wstring wstr(len, 0); - MultiByteToWideChar(CP_UTF8, 0, file_name_utf8, -1, &wstr[0], len); + MultiByteToWideChar(CP_UTF8, 0, file_name.c_str(), -1, &wstr[0], len); FILE* file = _wfopen(wstr.c_str(), read_only ? L"rb" : L"wb"); #else - FILE* file = fopen(file_name_utf8, read_only ? "rb" : "wb"); + FILE* file = fopen(file_name.c_str(), read_only ? "rb" : "wb"); #endif if (!file && error) { *error = errno; @@ -38,36 +43,19 @@ FILE* FileOpen(const char* file_name_utf8, bool read_only, int* error) { return file; } -const char* GetCstrCheckNoEmbeddedNul(const std::string& s) { - const char* p = s.c_str(); - RTC_CHECK_EQ(strlen(p), s.size()) - << "Invalid filename, containing NUL character"; - return p; -} } // namespace // static -FileWrapper FileWrapper::OpenReadOnly(const char* file_name_utf8) { +FileWrapper FileWrapper::OpenReadOnly(absl::string_view file_name_utf8) { return FileWrapper(FileOpen(file_name_utf8, true, nullptr)); } // static -FileWrapper FileWrapper::OpenReadOnly(const std::string& file_name_utf8) { - return OpenReadOnly(GetCstrCheckNoEmbeddedNul(file_name_utf8)); -} - -// static -FileWrapper FileWrapper::OpenWriteOnly(const char* file_name_utf8, +FileWrapper FileWrapper::OpenWriteOnly(absl::string_view file_name_utf8, int* error /*=nullptr*/) { return FileWrapper(FileOpen(file_name_utf8, false, error)); } -// static -FileWrapper FileWrapper::OpenWriteOnly(const std::string& file_name_utf8, - int* error /*=nullptr*/) { - return OpenWriteOnly(GetCstrCheckNoEmbeddedNul(file_name_utf8), error); -} - FileWrapper::FileWrapper(FileWrapper&& other) { operator=(std::move(other)); } diff --git a/rtc_base/system/file_wrapper.h b/rtc_base/system/file_wrapper.h index b55b0b9864..5e1e3d6a16 100644 --- a/rtc_base/system/file_wrapper.h +++ b/rtc_base/system/file_wrapper.h @@ -16,6 +16,8 @@ #include +#include "absl/strings/string_view.h" + // Implementation that can read (exclusive) or write from/to a file. namespace webrtc { @@ -34,11 +36,8 @@ class FileWrapper final { // returned object to check if the open operation was successful. On failure, // and if `error` is non-null, the system errno value is stored at |*error|. // The file is closed by the destructor. - static FileWrapper OpenReadOnly(const char* file_name_utf8); - static FileWrapper OpenReadOnly(const std::string& file_name_utf8); - static FileWrapper OpenWriteOnly(const char* file_name_utf8, - int* error = nullptr); - static FileWrapper OpenWriteOnly(const std::string& file_name_utf8, + static FileWrapper OpenReadOnly(absl::string_view file_name_utf8); + static FileWrapper OpenWriteOnly(absl::string_view file_name_utf8, int* error = nullptr); FileWrapper() = default; diff --git a/rtc_base/system/no_unique_address.h b/rtc_base/system/no_unique_address.h index 77e7a99526..6bede2c6b6 100644 --- a/rtc_base/system/no_unique_address.h +++ b/rtc_base/system/no_unique_address.h @@ -24,8 +24,10 @@ // 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. -#if ((defined(__clang__) && !defined(_MSC_VER) && !defined(WEBRTC_IOS)) || \ - __cplusplus > 201703L) +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(__x) 0 +#endif +#if __has_cpp_attribute(no_unique_address) // NOLINTNEXTLINE(whitespace/braces) #define RTC_NO_UNIQUE_ADDRESS [[no_unique_address]] #else diff --git a/rtc_base/system/thread_registry.cc b/rtc_base/system/thread_registry.cc deleted file mode 100644 index b0e83ca1e9..0000000000 --- a/rtc_base/system/thread_registry.cc +++ /dev/null @@ -1,71 +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. - */ - -#include "rtc_base/system/thread_registry.h" - -#include -#include - -#include "absl/base/attributes.h" -#include "rtc_base/logging.h" -#include "rtc_base/platform_thread_types.h" -#include "rtc_base/synchronization/mutex.h" -#include "sdk/android/native_api/stacktrace/stacktrace.h" - -namespace webrtc { - -namespace { - -struct ThreadData { - const rtc::PlatformThreadId thread_id; - const rtc::Location location; -}; - -// The map of registered threads, and the lock that protects it. We create the -// map on first use, and never destroy it. -ABSL_CONST_INIT GlobalMutex g_thread_registry_lock(absl::kConstInit); -ABSL_CONST_INIT std::map* - g_registered_threads = nullptr; - -} // namespace - -ScopedRegisterThreadForDebugging::ScopedRegisterThreadForDebugging( - rtc::Location location) { - GlobalMutexLock gls(&g_thread_registry_lock); - if (g_registered_threads == nullptr) { - g_registered_threads = - new std::map(); - } - const auto result = g_registered_threads->insert( - std::make_pair(this, ThreadData{rtc::CurrentThreadId(), location})); - RTC_DCHECK(result.second); // Insertion succeeded without collisions. -} - -ScopedRegisterThreadForDebugging::~ScopedRegisterThreadForDebugging() { - GlobalMutexLock gls(&g_thread_registry_lock); - RTC_DCHECK(g_registered_threads != nullptr); - const int num_erased = g_registered_threads->erase(this); - RTC_DCHECK_EQ(num_erased, 1); -} - -void PrintStackTracesOfRegisteredThreads() { - GlobalMutexLock gls(&g_thread_registry_lock); - if (g_registered_threads == nullptr) { - return; - } - for (const auto& e : *g_registered_threads) { - const ThreadData& td = e.second; - RTC_LOG(LS_WARNING) << "Thread " << td.thread_id << " registered at " - << td.location.ToString() << ":"; - RTC_LOG(LS_WARNING) << StackTraceToString(GetStackTrace(td.thread_id)); - } -} - -} // namespace webrtc diff --git a/rtc_base/system/thread_registry.h b/rtc_base/system/thread_registry.h deleted file mode 100644 index 0e3187b884..0000000000 --- a/rtc_base/system/thread_registry.h +++ /dev/null @@ -1,46 +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_SYSTEM_THREAD_REGISTRY_H_ -#define RTC_BASE_SYSTEM_THREAD_REGISTRY_H_ - -#include "rtc_base/location.h" - -namespace webrtc { - -class ScopedRegisterThreadForDebugging { - public: -#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) - explicit ScopedRegisterThreadForDebugging(rtc::Location location); - ~ScopedRegisterThreadForDebugging(); -#else - explicit ScopedRegisterThreadForDebugging(rtc::Location) {} -#endif - - // Not movable or copyable, because we can't duplicate the resource it owns, - // and it needs a constant address. - ScopedRegisterThreadForDebugging(const ScopedRegisterThreadForDebugging&) = - delete; - ScopedRegisterThreadForDebugging(ScopedRegisterThreadForDebugging&&) = delete; - ScopedRegisterThreadForDebugging& operator=( - const ScopedRegisterThreadForDebugging&) = delete; - ScopedRegisterThreadForDebugging& operator=( - ScopedRegisterThreadForDebugging&&) = delete; -}; - -#if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) -void PrintStackTracesOfRegisteredThreads(); -#else -inline void PrintStackTracesOfRegisteredThreads() {} -#endif - -} // namespace webrtc - -#endif // RTC_BASE_SYSTEM_THREAD_REGISTRY_H_ diff --git a/rtc_base/task_queue.cc b/rtc_base/task_queue.cc index f0dbdb3ae0..0b54178405 100644 --- a/rtc_base/task_queue.cc +++ b/rtc_base/task_queue.cc @@ -30,18 +30,25 @@ bool TaskQueue::IsCurrent() const { } void TaskQueue::PostTask(std::unique_ptr task) { - return impl_->PostTask(std::move(task)); + impl_->PostTask(std::move(task)); } void TaskQueue::PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) { - return impl_->PostDelayedTask(std::move(task), milliseconds); + impl_->PostDelayedTask(std::move(task), milliseconds); } void TaskQueue::PostDelayedHighPrecisionTask( std::unique_ptr task, uint32_t milliseconds) { - return impl_->PostDelayedHighPrecisionTask(std::move(task), milliseconds); + impl_->PostDelayedHighPrecisionTask(std::move(task), milliseconds); +} + +void TaskQueue::PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds) { + impl_->PostDelayedTaskWithPrecision(precision, std::move(task), milliseconds); } } // namespace rtc diff --git a/rtc_base/task_queue.h b/rtc_base/task_queue.h index 4ad3fedd95..ef97646988 100644 --- a/rtc_base/task_queue.h +++ b/rtc_base/task_queue.h @@ -100,6 +100,10 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueue { uint32_t milliseconds); void PostDelayedHighPrecisionTask(std::unique_ptr task, uint32_t milliseconds); + void PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds); // std::enable_if is used here to make sure that calls to PostTask() with // std::unique_ptr would not end up being @@ -127,6 +131,18 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueue { PostDelayedHighPrecisionTask( webrtc::ToQueuedTask(std::forward(closure)), milliseconds); } + template >::value>::type* = nullptr> + void PostDelayedTaskWithPrecision( + webrtc::TaskQueueBase::DelayPrecision precision, + Closure&& closure, + uint32_t milliseconds) { + PostDelayedTaskWithPrecision( + precision, webrtc::ToQueuedTask(std::forward(closure)), + milliseconds); + } private: webrtc::TaskQueueBase* const impl_; diff --git a/rtc_base/task_queue_stdlib.cc b/rtc_base/task_queue_stdlib.cc index 41da285ee7..946261ed58 100644 --- a/rtc_base/task_queue_stdlib.cc +++ b/rtc_base/task_queue_stdlib.cc @@ -68,31 +68,32 @@ class TaskQueueStdlib final : public TaskQueueBase { }; struct NextTask { - bool final_task_{false}; + bool final_task_ = false; std::unique_ptr run_task_; - int64_t sleep_time_ms_{}; + int64_t sleep_time_ms_ = 0; }; + static rtc::PlatformThread InitializeThread(TaskQueueStdlib* me, + absl::string_view queue_name, + rtc::ThreadPriority priority); + NextTask GetNextTask(); void ProcessTasks(); void NotifyWake(); - // Indicates if the thread has started. - rtc::Event started_; - // Signaled whenever a new task is pending. rtc::Event flag_notify_; Mutex pending_lock_; // Indicates if the worker thread needs to shutdown now. - bool thread_should_quit_ RTC_GUARDED_BY(pending_lock_){false}; + bool thread_should_quit_ RTC_GUARDED_BY(pending_lock_) = false; // Holds the next order to use for the next task to be // put into one of the pending queues. - OrderId thread_posting_order_ RTC_GUARDED_BY(pending_lock_){}; + OrderId thread_posting_order_ RTC_GUARDED_BY(pending_lock_) = 0; // The list of all pending tasks that need to be processed in the // FIFO queue ordering on the worker thread. @@ -117,16 +118,24 @@ class TaskQueueStdlib final : public TaskQueueBase { TaskQueueStdlib::TaskQueueStdlib(absl::string_view queue_name, rtc::ThreadPriority priority) - : started_(/*manual_reset=*/false, /*initially_signaled=*/false), - flag_notify_(/*manual_reset=*/false, /*initially_signaled=*/false), - thread_(rtc::PlatformThread::SpawnJoinable( - [this] { - CurrentTaskQueueSetter set_current(this); - ProcessTasks(); - }, - queue_name, - rtc::ThreadAttributes().SetPriority(priority))) { - started_.Wait(rtc::Event::kForever); + : flag_notify_(/*manual_reset=*/false, /*initially_signaled=*/false), + thread_(InitializeThread(this, queue_name, priority)) {} + +// static +rtc::PlatformThread TaskQueueStdlib::InitializeThread( + TaskQueueStdlib* me, + absl::string_view queue_name, + rtc::ThreadPriority priority) { + rtc::Event started; + auto thread = rtc::PlatformThread::SpawnJoinable( + [&started, me] { + CurrentTaskQueueSetter set_current(me); + started.Set(); + me->ProcessTasks(); + }, + queue_name, rtc::ThreadAttributes().SetPriority(priority)); + started.Wait(rtc::Event::kForever); + return thread; } void TaskQueueStdlib::Delete() { @@ -156,7 +165,7 @@ void TaskQueueStdlib::PostTask(std::unique_ptr task) { void TaskQueueStdlib::PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) { - auto fire_at = rtc::TimeMillis() + milliseconds; + const auto fire_at = rtc::TimeMillis() + milliseconds; DelayedEntryTimeout delay; delay.next_fire_at_ms_ = fire_at; @@ -171,9 +180,9 @@ void TaskQueueStdlib::PostDelayedTask(std::unique_ptr task, } TaskQueueStdlib::NextTask TaskQueueStdlib::GetNextTask() { - NextTask result{}; + NextTask result; - auto tick = rtc::TimeMillis(); + const auto tick = rtc::TimeMillis(); MutexLock lock(&pending_lock_); @@ -216,8 +225,6 @@ TaskQueueStdlib::NextTask TaskQueueStdlib::GetNextTask() { } void TaskQueueStdlib::ProcessTasks() { - started_.Set(); - while (true) { auto task = GetNextTask(); @@ -230,14 +237,12 @@ void TaskQueueStdlib::ProcessTasks() { if (release_ptr->Run()) delete release_ptr; - // attempt to sleep again + // Attempt to run more tasks before going to sleep. continue; } - if (0 == task.sleep_time_ms_) - flag_notify_.Wait(rtc::Event::kForever); - else - flag_notify_.Wait(task.sleep_time_ms_); + flag_notify_.Wait(0 == task.sleep_time_ms_ ? rtc::Event::kForever + : task.sleep_time_ms_); } } diff --git a/rtc_base/task_utils/BUILD.gn b/rtc_base/task_utils/BUILD.gn index 8767e9ecdc..893031dfd1 100644 --- a/rtc_base/task_utils/BUILD.gn +++ b/rtc_base/task_utils/BUILD.gn @@ -56,7 +56,8 @@ if (rtc_include_tests) { deps = [ ":pending_task_safety_flag", ":to_queued_task", - "..:rtc_base_approved", + "..:logging", + "..:rtc_event", "..:rtc_task_queue", "..:task_queue_for_test", "../../test:test_support", @@ -69,7 +70,7 @@ if (rtc_include_tests) { deps = [ ":repeating_task", ":to_queued_task", - "..:rtc_base_approved", + "..:rtc_event", "..:rtc_task_queue", "..:task_queue_for_test", "../../api/task_queue", diff --git a/rtc_base/task_utils/repeating_task.h b/rtc_base/task_utils/repeating_task.h index 20f28d54f4..1086c1629e 100644 --- a/rtc_base/task_utils/repeating_task.h +++ b/rtc_base/task_utils/repeating_task.h @@ -75,8 +75,8 @@ class RepeatingTaskImpl final : public RepeatingTaskBase { closure_(std::forward(closure)) { static_assert( std::is_same::type>::value, + typename std::invoke_result::type>::value, ""); } diff --git a/rtc_base/test_echo_server.h b/rtc_base/test_echo_server.h index ba5f997287..e4f70caee2 100644 --- a/rtc_base/test_echo_server.h +++ b/rtc_base/test_echo_server.h @@ -45,7 +45,8 @@ class TestEchoServer : public sigslot::has_slots<> { if (raw_socket) { AsyncTCPSocket* packet_socket = new AsyncTCPSocket(raw_socket); packet_socket->SignalReadPacket.connect(this, &TestEchoServer::OnPacket); - packet_socket->SignalClose.connect(this, &TestEchoServer::OnClose); + packet_socket->SubscribeClose( + this, [this](AsyncPacketSocket* s, int err) { OnClose(s, err); }); client_sockets_.push_back(packet_socket); } } diff --git a/rtc_base/third_party/base64/BUILD.gn b/rtc_base/third_party/base64/BUILD.gn index 969c7c0c64..29e7fce118 100644 --- a/rtc_base/third_party/base64/BUILD.gn +++ b/rtc_base/third_party/base64/BUILD.gn @@ -18,4 +18,5 @@ rtc_library("base64") { "../..:checks", "../../system:rtc_export", ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } diff --git a/rtc_base/third_party/base64/base64.cc b/rtc_base/third_party/base64/base64.cc index 7e30b0eb34..9dc961ae52 100644 --- a/rtc_base/third_party/base64/base64.cc +++ b/rtc_base/third_party/base64/base64.cc @@ -18,6 +18,7 @@ #include +#include "absl/strings/string_view.h" #include "rtc_base/checks.h" using std::vector; @@ -85,7 +86,7 @@ bool Base64::GetNextBase64Char(char ch, char* next_ch) { return true; } -bool Base64::IsBase64Encoded(const std::string& str) { +bool Base64::IsBase64Encoded(absl::string_view str) { for (size_t i = 0; i < str.size(); ++i) { if (!IsBase64Char(str.at(i))) return false; diff --git a/rtc_base/third_party/base64/base64.h b/rtc_base/third_party/base64/base64.h index ca249541d0..4190a79dc0 100644 --- a/rtc_base/third_party/base64/base64.h +++ b/rtc_base/third_party/base64/base64.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/system/rtc_export.h" namespace rtc { @@ -56,7 +57,7 @@ class Base64 { // Determines whether the given string consists entirely of valid base64 // encoded characters. - static bool IsBase64Encoded(const std::string& str); + static bool IsBase64Encoded(absl::string_view str); RTC_EXPORT static void EncodeFromArray(const void* data, size_t len, @@ -78,23 +79,23 @@ class Base64 { size_t* data_used); // Convenience Methods - static inline std::string Encode(const std::string& data) { + static inline std::string Encode(absl::string_view data) { std::string result; EncodeFromArray(data.data(), data.size(), &result); return result; } - static inline std::string Decode(const std::string& data, DecodeFlags flags) { + static inline std::string Decode(absl::string_view data, DecodeFlags flags) { std::string result; DecodeFromArray(data.data(), data.size(), flags, &result, nullptr); return result; } - static inline bool Decode(const std::string& data, + static inline bool Decode(absl::string_view data, DecodeFlags flags, std::string* result, size_t* data_used) { return DecodeFromArray(data.data(), data.size(), flags, result, data_used); } - static inline bool Decode(const std::string& data, + static inline bool Decode(absl::string_view data, DecodeFlags flags, std::vector* result, size_t* data_used) { diff --git a/rtc_base/thread.cc b/rtc_base/thread.cc index 307d499255..f9680650a9 100644 --- a/rtc_base/thread.cc +++ b/rtc_base/thread.cc @@ -10,6 +10,8 @@ #include "rtc_base/thread.h" +#include "absl/strings/string_view.h" + #if defined(WEBRTC_WIN) #include #elif defined(WEBRTC_POSIX) @@ -744,10 +746,10 @@ bool Thread::SleepMs(int milliseconds) { #endif } -bool Thread::SetName(const std::string& name, const void* obj) { +bool Thread::SetName(absl::string_view name, const void* obj) { RTC_DCHECK(!IsRunning()); - name_ = name; + name_ = std::string(name); if (obj) { // The %p specifier typically produce at most 16 hex digits, possibly with a // 0x prefix. But format is implementation defined, so add some margin. diff --git a/rtc_base/thread.h b/rtc_base/thread.h index 3c4ed558cd..052a71b22c 100644 --- a/rtc_base/thread.h +++ b/rtc_base/thread.h @@ -22,6 +22,8 @@ #include #include +#include "absl/strings/string_view.h" + #if defined(WEBRTC_POSIX) #include #endif @@ -348,7 +350,7 @@ class RTC_LOCKABLE RTC_EXPORT Thread : public webrtc::TaskQueueBase { // Sets the thread's name, for debugging. Must be called before Start(). // If `obj` is non-null, its value is appended to `name`. const std::string& name() const { return name_; } - bool SetName(const std::string& name, const void* obj); + bool SetName(absl::string_view name, const void* obj); // Sets the expected processing time in ms. The thread will write // log messages when Invoke() takes more time than this. diff --git a/rtc_base/time/BUILD.gn b/rtc_base/time/BUILD.gn index 9a1d99b610..bac649e1d7 100644 --- a/rtc_base/time/BUILD.gn +++ b/rtc_base/time/BUILD.gn @@ -17,4 +17,25 @@ rtc_library("timestamp_extrapolator") { "timestamp_extrapolator.cc", "timestamp_extrapolator.h", ] + deps = [ + "../../api/units:timestamp", + "../../modules:module_api_public", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +if (rtc_include_tests) { + rtc_library("timestamp_extrapolator_unittests") { + testonly = true + sources = [ "timestamp_extrapolator_unittest.cc" ] + deps = [ + ":timestamp_extrapolator", + "../../api/units:frequency", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../system_wrappers", + "../../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } } diff --git a/rtc_base/time/timestamp_extrapolator.cc b/rtc_base/time/timestamp_extrapolator.cc index 99445284dc..4f8fe0a914 100644 --- a/rtc_base/time/timestamp_extrapolator.cc +++ b/rtc_base/time/timestamp_extrapolator.cc @@ -12,84 +12,83 @@ #include +#include "absl/types/optional.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { -TimestampExtrapolator::TimestampExtrapolator(int64_t start_ms) - : _startMs(0), - _firstTimestamp(0), - _wrapArounds(0), - _prevUnwrappedTimestamp(-1), - _prevWrapTimestamp(-1), - _lambda(1), - _firstAfterReset(true), - _packetCount(0), - _startUpFilterDelayInPackets(2), - _detectorAccumulatorPos(0), - _detectorAccumulatorNeg(0), - _alarmThreshold(60e3), - _accDrift(6600), // in timestamp ticks, i.e. 15 ms - _accMaxError(7000), - _pP11(1e10) { - Reset(start_ms); +namespace { + +constexpr double kLambda = 1; +constexpr uint32_t kStartUpFilterDelayInPackets = 2; +constexpr double kAlarmThreshold = 60e3; +// in timestamp ticks, i.e. 15 ms +constexpr double kAccDrift = 6600; +constexpr double kAccMaxError = 7000; +constexpr double kP11 = 1e10; + +} // namespace + +TimestampExtrapolator::TimestampExtrapolator(Timestamp start) + : start_(Timestamp::Zero()), + prev_(Timestamp::Zero()), + packet_count_(0), + detector_accumulator_pos_(0), + detector_accumulator_neg_(0) { + Reset(start); } -void TimestampExtrapolator::Reset(int64_t start_ms) { - _startMs = start_ms; - _prevMs = _startMs; - _firstTimestamp = 0; - _w[0] = 90.0; - _w[1] = 0; - _pP[0][0] = 1; - _pP[1][1] = _pP11; - _pP[0][1] = _pP[1][0] = 0; - _firstAfterReset = true; - _prevUnwrappedTimestamp = -1; - _prevWrapTimestamp = -1; - _wrapArounds = 0; - _packetCount = 0; - _detectorAccumulatorPos = 0; - _detectorAccumulatorNeg = 0; +void TimestampExtrapolator::Reset(Timestamp start) { + start_ = start; + prev_ = start_; + first_unwrapped_timestamp_ = absl::nullopt; + w_[0] = 90.0; + w_[1] = 0; + p_[0][0] = 1; + p_[1][1] = kP11; + p_[0][1] = p_[1][0] = 0; + unwrapper_ = TimestampUnwrapper(); + packet_count_ = 0; + detector_accumulator_pos_ = 0; + detector_accumulator_neg_ = 0; } -void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { - if (tMs - _prevMs > 10e3) { +void TimestampExtrapolator::Update(Timestamp now, uint32_t ts90khz) { + if (now - prev_ > TimeDelta::Seconds(10)) { // Ten seconds without a complete frame. // Reset the extrapolator - Reset(tMs); + Reset(now); } else { - _prevMs = tMs; + prev_ = now; } // Remove offset to prevent badly scaled matrices - tMs -= _startMs; - - CheckForWrapArounds(ts90khz); + const TimeDelta offset = now - start_; + double t_ms = offset.ms(); - int64_t unwrapped_ts90khz = - static_cast(ts90khz) + - _wrapArounds * ((static_cast(1) << 32) - 1); + int64_t unwrapped_ts90khz = unwrapper_.Unwrap(ts90khz); - if (_firstAfterReset) { + if (!first_unwrapped_timestamp_) { // Make an initial guess of the offset, - // should be almost correct since tMs - _startMs + // should be almost correct since t_ms - start // should about zero at this time. - _w[1] = -_w[0] * tMs; - _firstTimestamp = unwrapped_ts90khz; - _firstAfterReset = false; + w_[1] = -w_[0] * t_ms; + first_unwrapped_timestamp_ = unwrapped_ts90khz; } - double residual = (static_cast(unwrapped_ts90khz) - _firstTimestamp) - - static_cast(tMs) * _w[0] - _w[1]; + double residual = + (static_cast(unwrapped_ts90khz) - *first_unwrapped_timestamp_) - + t_ms * w_[0] - w_[1]; if (DelayChangeDetection(residual) && - _packetCount >= _startUpFilterDelayInPackets) { + packet_count_ >= kStartUpFilterDelayInPackets) { // A sudden change of average network delay has been detected. // Force the filter to adjust its offset parameter by changing // the offset uncertainty. Don't do this during startup. - _pP[1][1] = _pP11; + p_[1][1] = kP11; } - if (_prevUnwrappedTimestamp >= 0 && - unwrapped_ts90khz < _prevUnwrappedTimestamp) { + if (prev_unwrapped_timestamp_ && + unwrapped_ts90khz < prev_unwrapped_timestamp_) { // Drop reordered frames. return; } @@ -98,98 +97,63 @@ void TimestampExtrapolator::Update(int64_t tMs, uint32_t ts90khz) { // that = T'*w; // K = P*T/(lambda + T'*P*T); double K[2]; - K[0] = _pP[0][0] * tMs + _pP[0][1]; - K[1] = _pP[1][0] * tMs + _pP[1][1]; - double TPT = _lambda + tMs * K[0] + K[1]; + K[0] = p_[0][0] * t_ms + p_[0][1]; + K[1] = p_[1][0] * t_ms + p_[1][1]; + double TPT = kLambda + t_ms * K[0] + K[1]; K[0] /= TPT; K[1] /= TPT; // w = w + K*(ts(k) - that); - _w[0] = _w[0] + K[0] * residual; - _w[1] = _w[1] + K[1] * residual; + w_[0] = w_[0] + K[0] * residual; + w_[1] = w_[1] + K[1] * residual; // P = 1/lambda*(P - K*T'*P); double p00 = - 1 / _lambda * (_pP[0][0] - (K[0] * tMs * _pP[0][0] + K[0] * _pP[1][0])); + 1 / kLambda * (p_[0][0] - (K[0] * t_ms * p_[0][0] + K[0] * p_[1][0])); double p01 = - 1 / _lambda * (_pP[0][1] - (K[0] * tMs * _pP[0][1] + K[0] * _pP[1][1])); - _pP[1][0] = - 1 / _lambda * (_pP[1][0] - (K[1] * tMs * _pP[0][0] + K[1] * _pP[1][0])); - _pP[1][1] = - 1 / _lambda * (_pP[1][1] - (K[1] * tMs * _pP[0][1] + K[1] * _pP[1][1])); - _pP[0][0] = p00; - _pP[0][1] = p01; - _prevUnwrappedTimestamp = unwrapped_ts90khz; - if (_packetCount < _startUpFilterDelayInPackets) { - _packetCount++; - } -} - -int64_t TimestampExtrapolator::ExtrapolateLocalTime(uint32_t timestamp90khz) { - int64_t localTimeMs = 0; - CheckForWrapArounds(timestamp90khz); - double unwrapped_ts90khz = - static_cast(timestamp90khz) + - _wrapArounds * ((static_cast(1) << 32) - 1); - if (_packetCount == 0) { - localTimeMs = -1; - } else if (_packetCount < _startUpFilterDelayInPackets) { - localTimeMs = - _prevMs + - static_cast( - static_cast(unwrapped_ts90khz - _prevUnwrappedTimestamp) / - 90.0 + - 0.5); - } else { - if (_w[0] < 1e-3) { - localTimeMs = _startMs; - } else { - double timestampDiff = - unwrapped_ts90khz - static_cast(_firstTimestamp); - localTimeMs = static_cast(static_cast(_startMs) + - (timestampDiff - _w[1]) / _w[0] + 0.5); - } + 1 / kLambda * (p_[0][1] - (K[0] * t_ms * p_[0][1] + K[0] * p_[1][1])); + p_[1][0] = + 1 / kLambda * (p_[1][0] - (K[1] * t_ms * p_[0][0] + K[1] * p_[1][0])); + p_[1][1] = + 1 / kLambda * (p_[1][1] - (K[1] * t_ms * p_[0][1] + K[1] * p_[1][1])); + p_[0][0] = p00; + p_[0][1] = p01; + prev_unwrapped_timestamp_ = unwrapped_ts90khz; + if (packet_count_ < kStartUpFilterDelayInPackets) { + packet_count_++; } - return localTimeMs; } -// Investigates if the timestamp clock has overflowed since the last timestamp -// and keeps track of the number of wrap arounds since reset. -void TimestampExtrapolator::CheckForWrapArounds(uint32_t ts90khz) { - if (_prevWrapTimestamp == -1) { - _prevWrapTimestamp = ts90khz; - return; - } - if (ts90khz < _prevWrapTimestamp) { - // This difference will probably be less than -2^31 if we have had a wrap - // around (e.g. timestamp = 1, _previousTimestamp = 2^32 - 1). Since it is - // casted to a Word32, it should be positive. - if (static_cast(ts90khz - _prevWrapTimestamp) > 0) { - // Forward wrap around - _wrapArounds++; - } +absl::optional TimestampExtrapolator::ExtrapolateLocalTime( + uint32_t timestamp90khz) const { + int64_t unwrapped_ts90khz = unwrapper_.UnwrapWithoutUpdate(timestamp90khz); + + if (!first_unwrapped_timestamp_) { + return absl::nullopt; + } else if (packet_count_ < kStartUpFilterDelayInPackets) { + constexpr double kRtpTicksPerMs = 90; + TimeDelta diff = TimeDelta::Millis( + (unwrapped_ts90khz - *prev_unwrapped_timestamp_) / kRtpTicksPerMs); + return prev_ + diff; + } else if (w_[0] < 1e-3) { + return start_; } else { - // This difference will probably be less than -2^31 if we have had a - // backward wrap around. Since it is casted to a Word32, it should be - // positive. - if (static_cast(_prevWrapTimestamp - ts90khz) > 0) { - // Backward wrap around - _wrapArounds--; - } + double timestampDiff = unwrapped_ts90khz - *first_unwrapped_timestamp_; + auto diff_ms = static_cast((timestampDiff - w_[1]) / w_[0] + 0.5); + return start_ + TimeDelta::Millis(diff_ms); } - _prevWrapTimestamp = ts90khz; } bool TimestampExtrapolator::DelayChangeDetection(double error) { // CUSUM detection of sudden delay changes - error = (error > 0) ? std::min(error, _accMaxError) - : std::max(error, -_accMaxError); - _detectorAccumulatorPos = - std::max(_detectorAccumulatorPos + error - _accDrift, double{0}); - _detectorAccumulatorNeg = - std::min(_detectorAccumulatorNeg + error + _accDrift, double{0}); - if (_detectorAccumulatorPos > _alarmThreshold || - _detectorAccumulatorNeg < -_alarmThreshold) { + error = (error > 0) ? std::min(error, kAccMaxError) + : std::max(error, -kAccMaxError); + detector_accumulator_pos_ = + std::max(detector_accumulator_pos_ + error - kAccDrift, double{0}); + detector_accumulator_neg_ = + std::min(detector_accumulator_neg_ + error + kAccDrift, double{0}); + if (detector_accumulator_pos_ > kAlarmThreshold || + detector_accumulator_neg_ < -kAlarmThreshold) { // Alarm - _detectorAccumulatorPos = _detectorAccumulatorNeg = 0; + detector_accumulator_pos_ = detector_accumulator_neg_ = 0; return true; } return false; diff --git a/rtc_base/time/timestamp_extrapolator.h b/rtc_base/time/timestamp_extrapolator.h index b325d2cbaa..23e7975453 100644 --- a/rtc_base/time/timestamp_extrapolator.h +++ b/rtc_base/time/timestamp_extrapolator.h @@ -13,38 +13,34 @@ #include +#include "absl/types/optional.h" +#include "api/units/timestamp.h" +#include "modules/include/module_common_types_public.h" + namespace webrtc { // Not thread safe. class TimestampExtrapolator { public: - explicit TimestampExtrapolator(int64_t start_ms); - void Update(int64_t tMs, uint32_t ts90khz); - int64_t ExtrapolateLocalTime(uint32_t timestamp90khz); - void Reset(int64_t start_ms); + explicit TimestampExtrapolator(Timestamp start); + void Update(Timestamp now, uint32_t ts90khz); + absl::optional ExtrapolateLocalTime(uint32_t timestamp90khz) const; + void Reset(Timestamp start); private: void CheckForWrapArounds(uint32_t ts90khz); bool DelayChangeDetection(double error); - double _w[2]; - double _pP[2][2]; - int64_t _startMs; - int64_t _prevMs; - uint32_t _firstTimestamp; - int32_t _wrapArounds; - int64_t _prevUnwrappedTimestamp; - int64_t _prevWrapTimestamp; - const double _lambda; - bool _firstAfterReset; - uint32_t _packetCount; - const uint32_t _startUpFilterDelayInPackets; - - double _detectorAccumulatorPos; - double _detectorAccumulatorNeg; - const double _alarmThreshold; - const double _accDrift; - const double _accMaxError; - const double _pP11; + + double w_[2]; + double p_[2][2]; + Timestamp start_; + Timestamp prev_; + absl::optional first_unwrapped_timestamp_; + TimestampUnwrapper unwrapper_; + absl::optional prev_unwrapped_timestamp_; + uint32_t packet_count_; + double detector_accumulator_pos_; + double detector_accumulator_neg_; }; } // namespace webrtc diff --git a/rtc_base/time/timestamp_extrapolator_unittest.cc b/rtc_base/time/timestamp_extrapolator_unittest.cc new file mode 100644 index 0000000000..b153d7ae15 --- /dev/null +++ b/rtc_base/time/timestamp_extrapolator_unittest.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2022 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/time/timestamp_extrapolator.h" + +#include + +#include + +#include "absl/types/optional.h" +#include "api/units/frequency.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +using ::testing::Eq; +using ::testing::Optional; + +namespace { + +constexpr Frequency kRtpHz = Frequency::KiloHertz(90); +constexpr Frequency k25Fps = Frequency::Hertz(25); +constexpr TimeDelta k25FpsDelay = 1 / k25Fps; + +} // namespace + +TEST(TimestampExtrapolatorTest, ExtrapolationOccursAfter2Packets) { + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + // No packets so no timestamp. + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(90000), Eq(absl::nullopt)); + + uint32_t rtp = 90000; + clock.AdvanceTime(k25FpsDelay); + // First result is a bit confusing since it is based off the "start" time, + // which is arbitrary. + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000), + Optional(clock.CurrentTime() + TimeDelta::Seconds(1))); +} + +TEST(TimestampExtrapolatorTest, ResetsAfter10SecondPause) { + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + uint32_t rtp = 90000; + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + + rtp += 10 * kRtpHz.hertz(); + clock.AdvanceTime(TimeDelta::Seconds(10) + TimeDelta::Micros(1)); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); +} + +TEST(TimestampExtrapolatorTest, TimestampExtrapolatesMultipleRtpWrapArounds) { + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + uint32_t rtp = std::numeric_limits::max(); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + + // One overflow. Static cast to avoid undefined behaviour with +=. + rtp += static_cast(kRtpHz / k25Fps); + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + + // Assert that extrapolation works across the boundary as expected. + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000), + Optional(clock.CurrentTime() + TimeDelta::Seconds(1))); + // This is not quite 1s since the math always rounds up. + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp - 90000), + Optional(clock.CurrentTime() - TimeDelta::Millis(999))); + + // In order to avoid a wrap arounds reset, add a packet every 10s until we + // overflow twice. + constexpr TimeDelta kRtpOverflowDelay = + std::numeric_limits::max() / kRtpHz; + const Timestamp overflow_time = clock.CurrentTime() + kRtpOverflowDelay * 2; + + while (clock.CurrentTime() < overflow_time) { + clock.AdvanceTime(TimeDelta::Seconds(10)); + // Static-cast before += to avoid undefined behaviour of overflow. + rtp += static_cast(kRtpHz * TimeDelta::Seconds(10)); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + } +} + +TEST(TimestampExtrapolatorTest, Slow90KHzClock) { + // This simulates a slow camera, which produces frames at 24Hz instead of + // 25Hz. The extrapolator should be able to resolve this with enough data. + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + constexpr TimeDelta k24FpsDelay = 1 / Frequency::Hertz(24); + uint32_t rtp = 90000; + ts_extrapolator.Update(clock.CurrentTime(), rtp); + + // Slow camera will increment RTP at 25 FPS rate even though its producing at + // 24 FPS. After 25 frames the extrapolator should settle at this rate. + for (int i = 0; i < 25; ++i) { + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k24FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + } + + // The camera would normally produce 25 frames in 90K ticks, but is slow + // so takes 1s + k24FpsDelay for 90K ticks. + constexpr Frequency kSlowRtpHz = 90000 / (25 * k24FpsDelay); + // The extrapolator will be predicting that time at millisecond precision. + auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz()); + ASSERT_TRUE(ts.has_value()); + EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000); +} + +TEST(TimestampExtrapolatorTest, Fast90KHzClock) { + // This simulates a fast camera, which produces frames at 26Hz instead of + // 25Hz. The extrapolator should be able to resolve this with enough data. + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + constexpr TimeDelta k26FpsDelay = 1 / Frequency::Hertz(26); + uint32_t rtp = 90000; + ts_extrapolator.Update(clock.CurrentTime(), rtp); + + // Fast camera will increment RTP at 25 FPS rate even though its producing at + // 26 FPS. After 25 frames the extrapolator should settle at this rate. + for (int i = 0; i < 25; ++i) { + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k26FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + } + + // The camera would normally produce 25 frames in 90K ticks, but is slow + // so takes 1s + k24FpsDelay for 90K ticks. + constexpr Frequency kSlowRtpHz = 90000 / (25 * k26FpsDelay); + // The extrapolator will be predicting that time at millisecond precision. + auto ts = ts_extrapolator.ExtrapolateLocalTime(rtp + kSlowRtpHz.hertz()); + ASSERT_TRUE(ts.has_value()); + EXPECT_EQ(ts->ms(), clock.TimeInMilliseconds() + 1000); +} + +TEST(TimestampExtrapolatorTest, TimestampJump) { + // This simulates a jump in RTP timestamp, which could occur if a camera was + // swapped for example. + SimulatedClock clock(Timestamp::Millis(1337)); + TimestampExtrapolator ts_extrapolator(clock.CurrentTime()); + + uint32_t rtp = 90000; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp), + Optional(clock.CurrentTime())); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(rtp + 90000), + Optional(clock.CurrentTime() + TimeDelta::Seconds(1))); + + // Jump RTP. + uint32_t new_rtp = 1337 * 90000; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), new_rtp); + new_rtp += kRtpHz / k25Fps; + clock.AdvanceTime(k25FpsDelay); + ts_extrapolator.Update(clock.CurrentTime(), new_rtp); + EXPECT_THAT(ts_extrapolator.ExtrapolateLocalTime(new_rtp), + Optional(clock.CurrentTime())); +} + +} // namespace webrtc diff --git a/rtc_base/unique_id_generator.cc b/rtc_base/unique_id_generator.cc index 9fa3021c6f..e68c643dbe 100644 --- a/rtc_base/unique_id_generator.cc +++ b/rtc_base/unique_id_generator.cc @@ -13,6 +13,7 @@ #include #include +#include "absl/strings/string_view.h" #include "rtc_base/helpers.h" #include "rtc_base/string_encode.h" #include "rtc_base/string_to_number.h" @@ -55,8 +56,11 @@ std::string UniqueStringGenerator::GenerateString() { return ToString(unique_number_generator_.GenerateNumber()); } -bool UniqueStringGenerator::AddKnownId(const std::string& value) { - absl::optional int_value = StringToNumber(value); +bool UniqueStringGenerator::AddKnownId(absl::string_view value) { + // TODO(webrtc:13579): remove string copy here once absl::string_view version + // of StringToNumber is available. + absl::optional int_value = + StringToNumber(std::string(value)); // The underlying generator works for uint32_t values, so if the provided // value is not a uint32_t it will never be generated anyway. if (int_value.has_value()) { diff --git a/rtc_base/unique_id_generator.h b/rtc_base/unique_id_generator.h index 3e2f9d7072..342dad7766 100644 --- a/rtc_base/unique_id_generator.h +++ b/rtc_base/unique_id_generator.h @@ -15,6 +15,7 @@ #include #include +#include "absl/strings/string_view.h" #include "api/array_view.h" #include "api/sequence_checker.h" #include "rtc_base/synchronization/mutex.h" @@ -103,7 +104,7 @@ class UniqueStringGenerator { // Adds an id that this generator should no longer generate. // Return value indicates whether the ID was hitherto unknown. - bool AddKnownId(const std::string& value); + bool AddKnownId(absl::string_view value); private: // This implementation will be simple and will generate "0", "1", ... diff --git a/rtc_base/units/unit_base.h b/rtc_base/units/unit_base.h index 4ccd9b750a..0cd6a61215 100644 --- a/rtc_base/units/unit_base.h +++ b/rtc_base/units/unit_base.h @@ -50,22 +50,22 @@ class UnitBase { return value_ == MinusInfinityVal(); } - constexpr bool operator==(const Unit_T& other) const { + constexpr bool operator==(const UnitBase& other) const { return value_ == other.value_; } - constexpr bool operator!=(const Unit_T& other) const { + constexpr bool operator!=(const UnitBase& other) const { return value_ != other.value_; } - constexpr bool operator<=(const Unit_T& other) const { + constexpr bool operator<=(const UnitBase& other) const { return value_ <= other.value_; } - constexpr bool operator>=(const Unit_T& other) const { + constexpr bool operator>=(const UnitBase& other) const { return value_ >= other.value_; } - constexpr bool operator>(const Unit_T& other) const { + constexpr bool operator>(const UnitBase& other) const { return value_ > other.value_; } - constexpr bool operator<(const Unit_T& other) const { + constexpr bool operator<(const UnitBase& other) const { return value_ < other.value_; } constexpr Unit_T RoundTo(const Unit_T& resolution) const { @@ -281,6 +281,9 @@ class RelativeUnit : public UnitBase { constexpr Unit_T operator*(int32_t scalar) const { return UnitBase::FromValue(this->ToValue() * scalar); } + constexpr Unit_T operator*(size_t scalar) const { + return UnitBase::FromValue(this->ToValue() * scalar); + } protected: using UnitBase::UnitBase; @@ -298,6 +301,11 @@ template inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit other) { return other * scalar; } +template +inline constexpr Unit_T operator*(size_t scalar, RelativeUnit other) { + return other * scalar; +} + template inline constexpr Unit_T operator-(RelativeUnit other) { if (other.IsPlusInfinity()) diff --git a/rtc_base/virtual_socket_server.cc b/rtc_base/virtual_socket_server.cc index 5d36e3e1de..edf9515711 100644 --- a/rtc_base/virtual_socket_server.cc +++ b/rtc_base/virtual_socket_server.cc @@ -612,7 +612,7 @@ void VirtualSocketServer::SetMessageQueue(Thread* msg_queue) { } bool VirtualSocketServer::Wait(int cmsWait, bool process_io) { - RTC_DCHECK(msg_queue_ == Thread::Current()); + RTC_DCHECK_RUN_ON(msg_queue_); if (stop_on_idle_ && Thread::Current()->empty()) { return false; } @@ -635,7 +635,7 @@ void VirtualSocketServer::SetAlternativeLocalAddress( } bool VirtualSocketServer::ProcessMessagesUntilIdle() { - RTC_DCHECK(msg_queue_ == Thread::Current()); + RTC_DCHECK_RUN_ON(msg_queue_); stop_on_idle_ = true; while (!msg_queue_->empty()) { if (fake_clock_) { @@ -785,6 +785,8 @@ static double Random() { int VirtualSocketServer::Connect(VirtualSocket* socket, const SocketAddress& remote_addr, bool use_delay) { + RTC_DCHECK(msg_queue_); + uint32_t delay = use_delay ? GetTransitDelay(socket) : 0; VirtualSocket* remote = LookupBinding(remote_addr); if (!CanInteractWith(socket, remote)) { @@ -805,15 +807,15 @@ int VirtualSocketServer::Connect(VirtualSocket* socket, } bool VirtualSocketServer::Disconnect(VirtualSocket* socket) { - if (socket) { - // If we simulate packets being delayed, we should simulate the - // equivalent of a FIN being delayed as well. - uint32_t delay = GetTransitDelay(socket); - // Remove the mapping. - msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT); - return true; - } - return false; + if (!socket || !msg_queue_) + return false; + + // If we simulate packets being delayed, we should simulate the + // equivalent of a FIN being delayed as well. + uint32_t delay = GetTransitDelay(socket); + // Remove the mapping. + msg_queue_->PostDelayed(RTC_FROM_HERE, delay, socket, MSG_ID_DISCONNECT); + return true; } bool VirtualSocketServer::Disconnect(const SocketAddress& addr) { @@ -871,6 +873,9 @@ void VirtualSocketServer::Clear(VirtualSocket* socket) { } void VirtualSocketServer::PostSignalReadEvent(VirtualSocket* socket) { + if (!msg_queue_) + return; + // 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); @@ -1011,6 +1016,7 @@ void VirtualSocketServer::AddPacketToNetwork(VirtualSocket* sender, size_t data_size, size_t header_size, bool ordered) { + RTC_DCHECK(msg_queue_); uint32_t send_delay = sender->AddPacket(cur_time, data_size + header_size); // Find the delay for crossing the many virtual hops of the network. diff --git a/rtc_base/win/BUILD.gn b/rtc_base/win/BUILD.gn new file mode 100644 index 0000000000..cf8bc21950 --- /dev/null +++ b/rtc_base/win/BUILD.gn @@ -0,0 +1,68 @@ +# 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. + +import("../../webrtc.gni") + +rtc_library("create_direct3d_device") { + sources = [ + "create_direct3d_device.cc", + "create_direct3d_device.h", + ] + data_deps = [ "//build/win:runtime_libs" ] +} + +rtc_library("get_activation_factory") { + sources = [ + "get_activation_factory.cc", + "get_activation_factory.h", + ] + data_deps = [ "//build/win:runtime_libs" ] + deps = [ ":hstring" ] +} + +rtc_library("hstring") { + sources = [ + "hstring.cc", + "hstring.h", + ] + data_deps = [ "//build/win:runtime_libs" ] +} + +rtc_library("scoped_com_initializer") { + sources = [ + "scoped_com_initializer.cc", + "scoped_com_initializer.h", + ] + data_deps = [ "//build/win:runtime_libs" ] + deps = [ + "..:checks", + "..:logging", + ] +} + +rtc_library("windows_version") { + sources = [ + "windows_version.cc", + "windows_version.h", + ] + data_deps = [ "//build/win:runtime_libs" ] + deps = [ + "..:checks", + "..:stringutils", + ] +} + +rtc_library("windows_version_unittest") { + testonly = true + sources = [ "windows_version_unittest.cc" ] + deps = [ + ":windows_version", + "..:gunit_helpers", + "..:logging", + ] +} diff --git a/rtc_base/win/windows_version.cc b/rtc_base/win/windows_version.cc index 80e49f2a16..93af1377be 100644 --- a/rtc_base/win/windows_version.cc +++ b/rtc_base/win/windows_version.cc @@ -211,9 +211,21 @@ Version MajorMinorBuildToVersion(int major, int minor, int build) { return VERSION_WIN10_19H1; } else if (build < 19041) { return VERSION_WIN10_19H2; - } else { + } else if (build < 19042) { return VERSION_WIN10_20H1; + } else if (build < 19043) { + return VERSION_WIN10_20H2; + } else if (build < 19044) { + return VERSION_WIN10_21H1; + } else if (build < 20348) { + return VERSION_WIN10_21H2; + } else if (build < 22000) { + return VERSION_SERVER_2022; + } else { + return VERSION_WIN11; } + } else if (major == 11) { + return VERSION_WIN11; } else if (major > 6) { RTC_DCHECK_NOTREACHED(); return VERSION_WIN_LAST; diff --git a/rtc_base/win/windows_version.h b/rtc_base/win/windows_version.h index 3636eabf19..8542626afb 100644 --- a/rtc_base/win/windows_version.h +++ b/rtc_base/win/windows_version.h @@ -30,24 +30,27 @@ namespace rtc_win { enum Version { VERSION_PRE_XP = 0, // Not supported. VERSION_XP = 1, - VERSION_SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2. - VERSION_VISTA = 3, // Also includes Windows Server 2008. - VERSION_WIN7 = 4, // Also includes Windows Server 2008 R2. - VERSION_WIN8 = 5, // Also includes Windows Server 2012. - VERSION_WIN8_1 = 6, // Also includes Windows Server 2012 R2. - VERSION_WIN10 = 7, // Threshold 1: Version 1507, Build 10240. - VERSION_WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586. - VERSION_WIN10_RS1 = 9, // Redstone 1: Version 1607, Build 14393. - VERSION_WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063. - VERSION_WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299. - VERSION_WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134. - VERSION_WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763. - VERSION_WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362. - VERSION_WIN10_19H2 = 15, // 19H2: Version 1909, Build 18363. - VERSION_WIN10_20H1 = 16, // 20H1 (Vibranium): Version 2004, Build 19041. - // On edit, update tools\metrics\histograms\enums.xml "WindowsVersion" and - // "GpuBlacklistFeatureTestResultsWindows2". - VERSION_WIN_LAST, // Indicates error condition. + VERSION_SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2. + VERSION_VISTA = 3, // Also includes Windows Server 2008. + VERSION_WIN7 = 4, // Also includes Windows Server 2008 R2. + VERSION_WIN8 = 5, // Also includes Windows Server 2012. + VERSION_WIN8_1 = 6, // Also includes Windows Server 2012 R2. + VERSION_WIN10 = 7, // Threshold 1: Version 1507, Build 10240. + VERSION_WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586. + VERSION_WIN10_RS1 = 9, // Redstone 1: Version 1607, Build 14393. + VERSION_WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063. + VERSION_WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299. + VERSION_WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134. + VERSION_WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763. + VERSION_WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362. + VERSION_WIN10_19H2 = 15, // 19H2: Version 1909, Build 18363. + VERSION_WIN10_20H1 = 16, // 20H1: Version 2004, Build 19041. + VERSION_WIN10_20H2 = 17, // 20H2: Build 19042. + VERSION_WIN10_21H1 = 18, // 21H1: Build 19043. + VERSION_WIN10_21H2 = 19, // 21H2: Build 19044. + VERSION_SERVER_2022 = 20, // Server 2022: Build 20348. + VERSION_WIN11 = 21, // Windows 11: Build 22000. + VERSION_WIN_LAST, // Indicates error condition. }; // A rough bucketing of the available types of versions of Windows. This is used diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index b841228a8e..e02f8d9040 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -47,6 +47,9 @@ group("rtc_tools") { ":unpack_aecdump", ] } + if (!build_with_chromium && rtc_enable_grpc) { + deps += [ "data_channel_benchmark" ] + } } rtc_library("video_file_reader") { @@ -59,7 +62,9 @@ rtc_library("video_file_reader") { "../api/video:video_frame", "../api/video:video_rtp_headers", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:refcount", + "../rtc_base:stringutils", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings", @@ -77,7 +82,7 @@ rtc_library("video_file_writer") { "../api:scoped_refptr", "../api/video:video_frame", "../api/video:video_rtp_headers", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings", @@ -107,7 +112,8 @@ rtc_library("video_quality_analysis") { "../api/video:video_rtp_headers", "../common_video", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", + "../rtc_base:logging", + "../rtc_base:refcount", "../test:perf_test", "//third_party/libyuv", ] @@ -199,7 +205,6 @@ if (!is_component_build) { "../media:rtc_audio_video", "../media:rtc_media_base", "../rtc_base", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_json", "../rtc_base:threading", "../rtc_base/system:file_wrapper", @@ -373,6 +378,10 @@ if (!build_with_chromium) { "../api:function_view", "../api:network_state_predictor_api", "../rtc_base:ignore_wundef", + "../rtc_base:logging", + "../rtc_base:macromagic", + "../rtc_base:rate_statistics", + "../rtc_base:refcount", # TODO(kwiberg): Remove this dependency. "../api/audio_codecs:audio_codecs_api", @@ -395,7 +404,6 @@ if (!build_with_chromium) { "../modules/rtp_rtcp", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", - "../rtc_base:rtc_base_approved", "../rtc_base:rtc_numerics", "../rtc_base:stringutils", "../system_wrappers", @@ -468,8 +476,8 @@ if (rtc_include_tests) { "../modules/audio_coding:neteq", "../modules/rtp_rtcp:rtp_rtcp_format", "../rtc_base:checks", + "../rtc_base:logging", "../rtc_base:protobuf_utils", - "../rtc_base:rtc_base_approved", "../system_wrappers:field_trial", "../test:field_trial", "../test:fileutils", @@ -558,7 +566,6 @@ if (rtc_include_tests) { "../api:audioproc_f_api", "../modules/audio_processing", "../modules/audio_processing:api", - "../rtc_base:rtc_base_approved", ] } @@ -576,8 +583,9 @@ if (rtc_include_tests) { "../modules/audio_processing:audioproc_protobuf_utils", "../modules/audio_processing:audioproc_test_utils", "../rtc_base:ignore_wundef", + "../rtc_base:macromagic", "../rtc_base:protobuf_utils", - "../rtc_base:rtc_base_approved", + "../rtc_base:stringutils", "//third_party/abseil-cpp/absl/flags:flag", "//third_party/abseil-cpp/absl/flags:parse", ] diff --git a/rtc_tools/data_channel_benchmark/BUILD.gn b/rtc_tools/data_channel_benchmark/BUILD.gn new file mode 100644 index 0000000000..cd8606ff92 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/BUILD.gn @@ -0,0 +1,63 @@ +# 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/grpc/grpc_library.gni") +import("../../webrtc.gni") + +grpc_library("signaling_grpc_proto") { + testonly = true + sources = [ "peer_connection_signaling.proto" ] +} + +rtc_library("signaling_interface") { + sources = [ "signaling_interface.h" ] + deps = [ "../../api:libjingle_peerconnection_api" ] +} + +rtc_library("grpc_signaling") { + testonly = true + sources = [ + "grpc_signaling.cc", + "grpc_signaling.h", + ] + deps = [ + ":signaling_grpc_proto", + ":signaling_interface", + "../../api:libjingle_peerconnection_api", + "../../rtc_base:threading", + "//third_party/grpc:grpc++", + ] + + defines = [ "GPR_FORBID_UNREACHABLE_CODE=0" ] +} + +rtc_executable("data_channel_benchmark") { + testonly = true + sources = [ + "data_channel_benchmark.cc", + "peer_connection_client.cc", + "peer_connection_client.h", + ] + deps = [ + ":grpc_signaling", + ":signaling_interface", + "../../api:create_peerconnection_factory", + "../../api:libjingle_peerconnection_api", + "../../api:rtc_error", + "../../api:scoped_refptr", + "../../api/audio_codecs:builtin_audio_decoder_factory", + "../../api/audio_codecs:builtin_audio_encoder_factory", + "../../api/video_codecs:builtin_video_decoder_factory", + "../../api/video_codecs:builtin_video_encoder_factory", + "../../rtc_base", + "../../rtc_base:logging", + "../../rtc_base:refcount", + "../../rtc_base:rtc_event", + "../../rtc_base:threading", + "../../system_wrappers:field_trial", + "//third_party/abseil-cpp/absl/cleanup:cleanup", + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] +} diff --git a/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc new file mode 100644 index 0000000000..e9531341b3 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/data_channel_benchmark.cc @@ -0,0 +1,323 @@ +/* + * 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. + * + * Data Channel Benchmarking tool. + * + * Create a server using: ./data_channel_benchmark --server --port 12345 + * Start the flow of data from the server to a client using: + * ./data_channel_benchmark --port 12345 --transfer_size 100 --packet_size 8196 + * The throughput is reported on the server console. + * + * The negotiation does not require a 3rd party server and is done over a gRPC + * transport. No TURN server is configured, so both peers need to be reachable + * using STUN only. + */ +#include + +#include + +#include "absl/cleanup/cleanup.h" +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "rtc_base/event.h" +#include "rtc_base/ssl_adapter.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/grpc_signaling.h" +#include "rtc_tools/data_channel_benchmark/peer_connection_client.h" +#include "system_wrappers/include/field_trial.h" + +ABSL_FLAG(int, verbose, 0, "verbosity level (0-5)"); +ABSL_FLAG(bool, server, false, "Server mode"); +ABSL_FLAG(bool, oneshot, true, "Terminate after serving a client"); +ABSL_FLAG(std::string, address, "localhost", "Connect to server address"); +ABSL_FLAG(uint16_t, port, 0, "Connect to port (0 for random)"); +ABSL_FLAG(uint64_t, transfer_size, 2, "Transfer size (MiB)"); +ABSL_FLAG(uint64_t, packet_size, 256 * 1024, "Packet size"); +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."); + +struct SetupMessage { + size_t packet_size; + size_t transfer_size; + + std::string ToString() { + char buffer[64]; + rtc::SimpleStringBuilder sb(buffer); + sb << packet_size << "," << transfer_size; + + return sb.str(); + } + + static SetupMessage FromString(absl::string_view sv) { + SetupMessage result; + auto parameters = rtc::split(sv, ','); + std::from_chars(parameters[0].data(), + parameters[0].data() + parameters[0].size(), + result.packet_size, 10); + std::from_chars(parameters[1].data(), + parameters[1].data() + parameters[1].size(), + result.transfer_size, 10); + return result; + } +}; + +class DataChannelObserverImpl : public webrtc::DataChannelObserver { + public: + explicit DataChannelObserverImpl(webrtc::DataChannelInterface* dc) + : dc_(dc), bytes_received_(0) {} + void OnStateChange() override { + RTC_LOG(LS_INFO) << "State changed to " << dc_->state(); + switch (dc_->state()) { + case webrtc::DataChannelInterface::DataState::kOpen: + open_event_.Set(); + break; + case webrtc::DataChannelInterface::DataState::kClosed: + closed_event_.Set(); + break; + default: + break; + } + } + void OnMessage(const webrtc::DataBuffer& buffer) override { + bytes_received_ += buffer.data.size(); + if (bytes_received_threshold_ && + bytes_received_ >= bytes_received_threshold_) { + bytes_received_event_.Set(); + } + + if (setup_message_.empty() && !buffer.binary) { + setup_message_.assign(buffer.data.cdata(), buffer.data.size()); + setup_message_event_.Set(); + } + } + void OnBufferedAmountChange(uint64_t sent_data_size) override { + if (dc_->buffered_amount() < + webrtc::DataChannelInterface::MaxSendQueueSize() / 2) + low_buffered_threshold_event_.Set(); + else + low_buffered_threshold_event_.Reset(); + } + + bool WaitForOpenState(int duration_ms) { + return dc_->state() == webrtc::DataChannelInterface::DataState::kOpen || + open_event_.Wait(duration_ms); + } + bool WaitForClosedState(int duration_ms) { + return dc_->state() == webrtc::DataChannelInterface::DataState::kClosed || + closed_event_.Wait(duration_ms); + } + + // Set how many received bytes are required until + // WaitForBytesReceivedThreshold return true. + void SetBytesReceivedThreshold(uint64_t bytes_received_threshold) { + bytes_received_threshold_ = bytes_received_threshold; + if (bytes_received_ >= bytes_received_threshold_) + bytes_received_event_.Set(); + } + // Wait until the received byte count reaches the desired value. + bool WaitForBytesReceivedThreshold(int duration_ms) { + return (bytes_received_threshold_ && + bytes_received_ >= bytes_received_threshold_) || + bytes_received_event_.Wait(duration_ms); + } + + bool WaitForLowbufferedThreshold(int duration_ms) { + return low_buffered_threshold_event_.Wait(duration_ms); + } + std::string SetupMessage() { return setup_message_; } + bool WaitForSetupMessage(int duration_ms) { + return setup_message_event_.Wait(duration_ms); + } + + private: + webrtc::DataChannelInterface* dc_; + rtc::Event open_event_; + rtc::Event closed_event_; + rtc::Event bytes_received_event_; + absl::optional bytes_received_threshold_; + uint64_t bytes_received_; + rtc::Event low_buffered_threshold_event_; + std::string setup_message_; + rtc::Event setup_message_event_; +}; + +int RunServer() { + bool oneshot = absl::GetFlag(FLAGS_oneshot); + uint16_t port = absl::GetFlag(FLAGS_port); + + auto signaling_thread = rtc::Thread::Create(); + signaling_thread->Start(); + { + auto factory = webrtc::PeerConnectionClient::CreateDefaultFactory( + signaling_thread.get()); + + auto grpc_server = webrtc::GrpcSignalingServerInterface::Create( + [factory = rtc::scoped_refptr( + factory)](webrtc::SignalingInterface* signaling) { + webrtc::PeerConnectionClient client(factory.get(), signaling); + client.StartPeerConnection(); + auto peer_connection = client.peerConnection(); + + // Set up the data channel + auto dc_or_error = + peer_connection->CreateDataChannelOrError("benchmark", nullptr); + auto data_channel = dc_or_error.MoveValue(); + auto data_channel_observer = + std::make_unique(data_channel.get()); + data_channel->RegisterObserver(data_channel_observer.get()); + absl::Cleanup unregister_observer( + [data_channel] { data_channel->UnregisterObserver(); }); + + // Wait for a first message from the remote peer. + // It configures how much data should be sent and how big the packets + // should be. + // First message is "packet_size,transfer_size". + data_channel_observer->WaitForSetupMessage(rtc::Event::kForever); + auto parameters = + SetupMessage::FromString(data_channel_observer->SetupMessage()); + + // Wait for the sender and receiver peers to stabilize (send all ACKs) + // This makes it easier to isolate the sending part when profiling. + absl::SleepFor(absl::Seconds(1)); + + std::string data(parameters.packet_size, '0'); + size_t remaining_data = parameters.transfer_size; + + auto begin_time = webrtc::Clock::GetRealTimeClock()->CurrentTime(); + + while (remaining_data) { + if (remaining_data < data.size()) + data.resize(remaining_data); + + rtc::CopyOnWriteBuffer buffer(data); + webrtc::DataBuffer data_buffer(buffer, true); + if (!data_channel->Send(data_buffer)) { + // If the send() call failed, the buffers are full. + // We wait until there's more room. + data_channel_observer->WaitForLowbufferedThreshold( + rtc::Event::kForever); + continue; + } + remaining_data -= buffer.size(); + fprintf(stderr, "Progress: %zu / %zu (%zu%%)\n", + (parameters.transfer_size - remaining_data), + parameters.transfer_size, + (100 - remaining_data * 100 / parameters.transfer_size)); + } + + // Receiver signals the data channel close event when it has received + // all the data it requested. + data_channel_observer->WaitForClosedState(rtc::Event::kForever); + + auto end_time = webrtc::Clock::GetRealTimeClock()->CurrentTime(); + auto duration_ms = (end_time - begin_time).ms(); + double throughput = (parameters.transfer_size / 1024. / 1024.) / + (duration_ms / 1000.); + printf("Elapsed time: %zums %gMiB/s\n", duration_ms, throughput); + }, + port, oneshot); + grpc_server->Start(); + + printf("Server listening on port %d\n", grpc_server->SelectedPort()); + grpc_server->Wait(); + } + + signaling_thread->Quit(); + return 0; +} + +int RunClient() { + uint16_t port = absl::GetFlag(FLAGS_port); + std::string server_address = absl::GetFlag(FLAGS_address); + size_t transfer_size = absl::GetFlag(FLAGS_transfer_size) * 1024 * 1024; + size_t packet_size = absl::GetFlag(FLAGS_packet_size); + + auto signaling_thread = rtc::Thread::Create(); + signaling_thread->Start(); + { + auto factory = webrtc::PeerConnectionClient::CreateDefaultFactory( + signaling_thread.get()); + auto grpc_client = webrtc::GrpcSignalingClientInterface::Create( + server_address + ":" + std::to_string(port)); + webrtc::PeerConnectionClient client(factory.get(), + grpc_client->signaling_client()); + + // Set up the callback to receive the data channel from the sender. + rtc::scoped_refptr data_channel; + rtc::Event got_data_channel; + client.SetOnDataChannel( + [&data_channel, &got_data_channel]( + rtc::scoped_refptr channel) { + data_channel = channel; + got_data_channel.Set(); + }); + + // Connect to the server. + if (!grpc_client->Start()) { + fprintf(stderr, "Failed to connect to server\n"); + return 1; + } + + // Wait for the data channel to be received + got_data_channel.Wait(rtc::Event::kForever); + + // DataChannel needs an observer to start draining the read queue + DataChannelObserverImpl observer(data_channel.get()); + observer.SetBytesReceivedThreshold(transfer_size); + data_channel->RegisterObserver(&observer); + absl::Cleanup unregister_observer( + [data_channel] { data_channel->UnregisterObserver(); }); + + // Send a configuration string to the server to tell it to send + // 'packet_size' bytes packets and send a total of 'transfer_size' MB. + observer.WaitForOpenState(rtc::Event::kForever); + SetupMessage setup_message = { + .packet_size = packet_size, + .transfer_size = transfer_size, + }; + if (!data_channel->Send(webrtc::DataBuffer(setup_message.ToString()))) { + fprintf(stderr, "Failed to send parameter string\n"); + return 1; + } + + // Wait until we have received all the data + observer.WaitForBytesReceivedThreshold(rtc::Event::kForever); + + // Close the data channel, signaling to the server we have received + // all the requested data. + data_channel->Close(); + } + + signaling_thread->Quit(); + + return 0; +} + +int main(int argc, char** argv) { + rtc::InitializeSSL(); + absl::ParseCommandLine(argc, argv); + + // Make sure that higher severity number means more logs by reversing the + // rtc::LoggingSeverity values. + auto logging_severity = + std::max(0, rtc::LS_NONE - absl::GetFlag(FLAGS_verbose)); + rtc::LogMessage::LogToDebug( + static_cast(logging_severity)); + + bool is_server = absl::GetFlag(FLAGS_server); + std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials); + + webrtc::field_trial::InitFieldTrialsFromString(field_trials.c_str()); + + return is_server ? RunServer() : RunClient(); +} diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.cc b/rtc_tools/data_channel_benchmark/grpc_signaling.cc new file mode 100644 index 0000000000..8db717fc71 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.cc @@ -0,0 +1,267 @@ +/* + * 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 "rtc_tools/data_channel_benchmark/grpc_signaling.h" + +#include +#include + +#include +#include + +#include "api/jsep.h" +#include "api/jsep_ice_candidate.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/peer_connection_signaling.grpc.pb.h" + +namespace webrtc { +namespace { + +using GrpcSignaling::IceCandidate; +using GrpcSignaling::PeerConnectionSignaling; +using GrpcSignaling::SessionDescription; +using GrpcSignaling::SignalingMessage; + +template +class SessionData : public webrtc::SignalingInterface { + public: + SessionData() {} + explicit SessionData(T* stream) : stream_(stream) {} + void SetStream(T* stream) { stream_ = stream; } + + void SendIceCandidate(const IceCandidateInterface* candidate) override { + RTC_LOG(LS_INFO) << "SendIceCandidate"; + std::string serialized_candidate; + if (!candidate->ToString(&serialized_candidate)) { + RTC_LOG(LS_ERROR) << "Failed to serialize ICE candidate"; + return; + } + + SignalingMessage message; + IceCandidate* proto_candidate = message.mutable_candidate(); + proto_candidate->set_description(serialized_candidate); + proto_candidate->set_mid(candidate->sdp_mid()); + proto_candidate->set_mline_index(candidate->sdp_mline_index()); + + stream_->Write(message); + } + + void SendDescription(const SessionDescriptionInterface* sdp) override { + RTC_LOG(LS_INFO) << "SendDescription"; + + std::string serialized_sdp; + sdp->ToString(&serialized_sdp); + + SignalingMessage message; + if (sdp->GetType() == SdpType::kOffer) + message.mutable_description()->set_type(SessionDescription::OFFER); + else if (sdp->GetType() == SdpType::kAnswer) + message.mutable_description()->set_type(SessionDescription::ANSWER); + message.mutable_description()->set_content(serialized_sdp); + + stream_->Write(message); + } + + void OnRemoteDescription( + std::function sdp)> + callback) override { + RTC_LOG(LS_INFO) << "OnRemoteDescription"; + remote_description_callback_ = callback; + } + + void OnIceCandidate( + std::function candidate)> + callback) override { + RTC_LOG(LS_INFO) << "OnIceCandidate"; + ice_candidate_callback_ = callback; + } + + T* stream_; + + std::function)> + ice_candidate_callback_; + std::function)> + remote_description_callback_; +}; + +using ServerSessionData = + SessionData>; +using ClientSessionData = + SessionData>; + +template +void ProcessMessages(StreamReader* stream, SessionData* session) { + MessageType message; + + while (stream->Read(&message)) { + switch (message.Content_case()) { + case SignalingMessage::ContentCase::kCandidate: { + webrtc::SdpParseError error; + auto jsep_candidate = std::make_unique( + message.candidate().mid(), message.candidate().mline_index()); + if (!jsep_candidate->Initialize(message.candidate().description(), + &error)) { + RTC_LOG(LS_ERROR) << "Failed to deserialize ICE candidate '" + << message.candidate().description() << "'"; + RTC_LOG(LS_ERROR) + << "Error at line " << error.line << ":" << error.description; + continue; + } + + session->ice_candidate_callback_(std::move(jsep_candidate)); + break; + } + case SignalingMessage::ContentCase::kDescription: { + auto& description = message.description(); + auto content = description.content(); + + auto sdp = webrtc::CreateSessionDescription( + description.type() == SessionDescription::OFFER + ? webrtc::SdpType::kOffer + : webrtc::SdpType::kAnswer, + description.content()); + session->remote_description_callback_(std::move(sdp)); + break; + } + default: + RTC_DCHECK_NOTREACHED(); + } + } +} + +class GrpcNegotiationServer : public GrpcSignalingServerInterface, + public PeerConnectionSignaling::Service { + public: + GrpcNegotiationServer( + std::function callback, + int port, + bool oneshot) + : connect_callback_(std::move(callback)), + requested_port_(port), + oneshot_(oneshot) {} + ~GrpcNegotiationServer() override { + Stop(); + if (server_stop_thread_) + server_stop_thread_->Stop(); + } + + void Start() override { + std::string server_address = "[::]"; + + grpc::ServerBuilder builder; + builder.AddListeningPort( + server_address + ":" + std::to_string(requested_port_), + grpc::InsecureServerCredentials(), &selected_port_); + builder.RegisterService(this); + server_ = builder.BuildAndStart(); + } + + void Wait() override { server_->Wait(); } + + void Stop() override { server_->Shutdown(); } + + int SelectedPort() override { return selected_port_; } + + grpc::Status Connect( + grpc::ServerContext* context, + grpc::ServerReaderWriter* stream) + override { + if (oneshot_) { + // Request the termination of the server early so we don't serve another + // client in parallel. + server_stop_thread_ = rtc::Thread::Create(); + server_stop_thread_->Start(); + server_stop_thread_->PostTask([this] { Stop(); }); + } + + ServerSessionData session(stream); + + auto reading_thread = rtc::Thread::Create(); + reading_thread->Start(); + reading_thread->PostTask([&session, &stream] { + ProcessMessages(stream, &session); + }); + + connect_callback_(&session); + + reading_thread->Stop(); + + return grpc::Status::OK; + } + + private: + std::function connect_callback_; + int requested_port_; + int selected_port_; + bool oneshot_; + + std::unique_ptr server_; + std::unique_ptr server_stop_thread_; +}; + +class GrpcNegotiationClient : public GrpcSignalingClientInterface { + public: + explicit GrpcNegotiationClient(const std::string& server) { + channel_ = grpc::CreateChannel(server, grpc::InsecureChannelCredentials()); + stub_ = PeerConnectionSignaling::NewStub(channel_); + } + + ~GrpcNegotiationClient() override { + context_.TryCancel(); + if (reading_thread_) + reading_thread_->Stop(); + } + + bool Start() override { + if (!channel_->WaitForConnected( + absl::ToChronoTime(absl::Now() + absl::Seconds(3)))) { + return false; + } + + stream_ = stub_->Connect(&context_); + session_.SetStream(stream_.get()); + + reading_thread_ = rtc::Thread::Create(); + reading_thread_->Start(); + reading_thread_->PostTask([this] { + ProcessMessages(stream_.get(), &session_); + }); + + return true; + } + + webrtc::SignalingInterface* signaling_client() override { return &session_; } + + private: + std::shared_ptr channel_; + std::unique_ptr stub_; + std::unique_ptr reading_thread_; + grpc::ClientContext context_; + std::unique_ptr< + ::grpc::ClientReaderWriter> + stream_; + ClientSessionData session_; +}; +} // namespace + +std::unique_ptr +GrpcSignalingServerInterface::Create( + std::function callback, + int port, + bool oneshot) { + return std::make_unique(std::move(callback), port, + oneshot); +} + +std::unique_ptr +GrpcSignalingClientInterface::Create(const std::string& server) { + return std::make_unique(server); +} + +} // namespace webrtc diff --git a/rtc_tools/data_channel_benchmark/grpc_signaling.h b/rtc_tools/data_channel_benchmark/grpc_signaling.h new file mode 100644 index 0000000000..15799d22b7 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/grpc_signaling.h @@ -0,0 +1,64 @@ +/* + * 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 RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ + +#include +#include + +#include "api/jsep.h" +#include "rtc_tools/data_channel_benchmark/signaling_interface.h" + +namespace webrtc { + +// This class defines a server enabling clients to perform a PeerConnection +// negotiation directly over gRPC. +// When a client connects, a callback is run to handle the request. +class GrpcSignalingServerInterface { + public: + virtual ~GrpcSignalingServerInterface() = default; + + // Start listening for connections. + virtual void Start() = 0; + + // Wait for the gRPC server to terminate. + virtual void Wait() = 0; + + // Stop the gRPC server instance. + virtual void Stop() = 0; + + // The port the server is listening on. + virtual int SelectedPort() = 0; + + // Create a gRPC server listening on |port| that will run |callback| on each + // request. If |oneshot| is true, it will terminate after serving one request. + static std::unique_ptr Create( + std::function callback, + int port, + bool oneshot); +}; + +// This class defines a client that can connect to a server and perform a +// PeerConnection negotiation directly over gRPC. +class GrpcSignalingClientInterface { + public: + virtual ~GrpcSignalingClientInterface() = default; + + // Connect the client to the gRPC server. + virtual bool Start() = 0; + virtual webrtc::SignalingInterface* signaling_client() = 0; + + // Create a client to connnect to a server at |server_address|. + static std::unique_ptr Create( + const std::string& server_address); +}; + +} // namespace webrtc +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_GRPC_SIGNALING_H_ diff --git a/rtc_tools/data_channel_benchmark/peer_connection_client.cc b/rtc_tools/data_channel_benchmark/peer_connection_client.cc new file mode 100644 index 0000000000..cd02e7118a --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_client.cc @@ -0,0 +1,300 @@ +/* + * 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 "rtc_tools/data_channel_benchmark/peer_connection_client.h" + +#include +#include +#include + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/create_peerconnection_factory.h" +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/scoped_refptr.h" +#include "api/set_remote_description_observer_interface.h" +#include "api/video_codecs/builtin_video_decoder_factory.h" +#include "api/video_codecs/builtin_video_encoder_factory.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +namespace { + +constexpr char kStunServer[] = "stun:stun.l.google.com:19302"; + +class SetLocalDescriptionObserverAdapter + : public webrtc::SetLocalDescriptionObserverInterface { + public: + using Callback = std::function; + static rtc::scoped_refptr Create( + Callback callback) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(callback))); + } + + explicit SetLocalDescriptionObserverAdapter(Callback callback) + : callback_(std::move(callback)) {} + ~SetLocalDescriptionObserverAdapter() override = default; + + private: + void OnSetLocalDescriptionComplete(webrtc::RTCError error) override { + callback_(std::move(error)); + } + + Callback callback_; +}; + +class SetRemoteDescriptionObserverAdapter + : public webrtc::SetRemoteDescriptionObserverInterface { + public: + using Callback = std::function; + static rtc::scoped_refptr Create( + Callback callback) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(callback))); + } + + explicit SetRemoteDescriptionObserverAdapter(Callback callback) + : callback_(std::move(callback)) {} + ~SetRemoteDescriptionObserverAdapter() override = default; + + private: + void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override { + callback_(std::move(error)); + } + + Callback callback_; +}; + +class CreateSessionDescriptionObserverAdapter + : public webrtc::CreateSessionDescriptionObserver { + public: + using Success = std::function; + using Failure = std::function; + + static rtc::scoped_refptr Create( + Success success, + Failure failure) { + return rtc::scoped_refptr( + new rtc::RefCountedObject( + std::move(success), std::move(failure))); + } + + CreateSessionDescriptionObserverAdapter(Success success, Failure failure) + : success_(std::move(success)), failure_(std::move(failure)) {} + ~CreateSessionDescriptionObserverAdapter() override = default; + + private: + void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { + success_(desc); + } + + void OnFailure(webrtc::RTCError error) override { + failure_(std::move(error)); + } + + Success success_; + Failure failure_; +}; + +} // namespace + +namespace webrtc { + +PeerConnectionClient::PeerConnectionClient( + webrtc::PeerConnectionFactoryInterface* factory, + webrtc::SignalingInterface* signaling) + : signaling_(signaling) { + signaling_->OnIceCandidate( + [&](std::unique_ptr candidate) { + AddIceCandidate(std::move(candidate)); + }); + signaling_->OnRemoteDescription( + [&](std::unique_ptr sdp) { + SetRemoteDescription(std::move(sdp)); + }); + InitializePeerConnection(factory); +} + +PeerConnectionClient::~PeerConnectionClient() { + Disconnect(); +} + +rtc::scoped_refptr +PeerConnectionClient::CreateDefaultFactory(rtc::Thread* signaling_thread) { + auto factory = webrtc::CreatePeerConnectionFactory( + /*network_thread=*/nullptr, /*worker_thread=*/nullptr, + /*signaling_thread*/ signaling_thread, + /*default_adm=*/nullptr, webrtc::CreateBuiltinAudioEncoderFactory(), + webrtc::CreateBuiltinAudioDecoderFactory(), + webrtc::CreateBuiltinVideoEncoderFactory(), + webrtc::CreateBuiltinVideoDecoderFactory(), + /*audio_mixer=*/nullptr, /*audio_processing=*/nullptr); + + if (!factory) { + RTC_LOG(LS_ERROR) << "Failed to initialize PeerConnectionFactory"; + return nullptr; + } + + return factory; +} + +bool PeerConnectionClient::InitializePeerConnection( + webrtc::PeerConnectionFactoryInterface* factory) { + RTC_CHECK(factory) + << "Must call InitializeFactory before InitializePeerConnection"; + + webrtc::PeerConnectionInterface::RTCConfiguration config; + webrtc::PeerConnectionInterface::IceServer server; + server.urls.push_back(kStunServer); + config.servers.push_back(server); + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + + webrtc::PeerConnectionDependencies dependencies(this); + auto result = + factory->CreatePeerConnectionOrError(config, std::move(dependencies)); + + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "Failed to create PeerConnection: " + << result.error().message(); + DeletePeerConnection(); + return false; + } + peer_connection_ = result.MoveValue(); + RTC_LOG(LS_INFO) << "PeerConnection created successfully"; + return true; +} + +bool PeerConnectionClient::StartPeerConnection() { + RTC_LOG(LS_INFO) << "Creating offer"; + + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create([this]( + webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription(peer_connection_->local_description()); + })); + + return true; +} + +bool PeerConnectionClient::IsConnected() { + return peer_connection_->peer_connection_state() == + webrtc::PeerConnectionInterface::PeerConnectionState::kConnected; +} + +// Disconnect from the call. +void PeerConnectionClient::Disconnect() { + for (auto& data_channel : data_channels_) { + data_channel->Close(); + data_channel.release(); + } + data_channels_.clear(); + DeletePeerConnection(); +} + +// Delete the WebRTC PeerConnection. +void PeerConnectionClient::DeletePeerConnection() { + RTC_LOG(LS_INFO); + + if (peer_connection_) { + peer_connection_->Close(); + } + peer_connection_.release(); +} + +void PeerConnectionClient::OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) { + if (new_state == webrtc::PeerConnectionInterface::IceConnectionState:: + kIceConnectionCompleted) { + RTC_LOG(LS_INFO) << "State is updating to connected"; + } else if (new_state == webrtc::PeerConnectionInterface::IceConnectionState:: + kIceConnectionDisconnected) { + RTC_LOG(LS_INFO) << "Disconnecting from peer"; + Disconnect(); + } +} + +void PeerConnectionClient::OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) { + if (new_state == webrtc::PeerConnectionInterface::kIceGatheringComplete) { + RTC_LOG(LS_INFO) << "Client is ready to receive remote SDP"; + } +} + +void PeerConnectionClient::OnIceCandidate( + const webrtc::IceCandidateInterface* candidate) { + signaling_->SendIceCandidate(candidate); +} + +void PeerConnectionClient::OnDataChannel( + rtc::scoped_refptr channel) { + RTC_LOG(LS_INFO) << __FUNCTION__ << " remote datachannel created"; + if (on_data_channel_callback_) + on_data_channel_callback_(channel); + data_channels_.push_back(channel); +} + +void PeerConnectionClient::SetOnDataChannel( + std::function)> + callback) { + on_data_channel_callback_ = callback; +} + +void PeerConnectionClient::OnNegotiationNeededEvent(uint32_t event_id) { + RTC_LOG(LS_INFO) << "OnNegotiationNeededEvent"; + + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create([this]( + webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription(peer_connection_->local_description()); + })); +} + +bool PeerConnectionClient::SetRemoteDescription( + std::unique_ptr desc) { + RTC_LOG(LS_INFO) << "SetRemoteDescription"; + auto type = desc->GetType(); + + peer_connection_->SetRemoteDescription( + std::move(desc), + SetRemoteDescriptionObserverAdapter::Create([&](webrtc::RTCError) { + RTC_LOG(LS_INFO) << "SetRemoteDescription done"; + + if (type == webrtc::SdpType::kOffer) { + // Got an offer from the remote, need to set an answer and send it. + peer_connection_->SetLocalDescription( + SetLocalDescriptionObserverAdapter::Create( + [this](webrtc::RTCError error) { + if (error.ok()) + signaling_->SendDescription( + peer_connection_->local_description()); + })); + } + })); + + return true; +} + +void PeerConnectionClient::AddIceCandidate( + std::unique_ptr candidate) { + RTC_LOG(LS_INFO) << "AddIceCandidate"; + + peer_connection_->AddIceCandidate( + std::move(candidate), [](const webrtc::RTCError& error) { + RTC_LOG(LS_INFO) << "Failed to add candidate: " << error.message(); + }); +} + +} // namespace webrtc diff --git a/rtc_tools/data_channel_benchmark/peer_connection_client.h b/rtc_tools/data_channel_benchmark/peer_connection_client.h new file mode 100644 index 0000000000..a9787fe709 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_client.h @@ -0,0 +1,108 @@ +/* + * 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 RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ + +#include + +#include +#include +#include + +#include "api/jsep.h" +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" +#include "api/set_local_description_observer_interface.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/thread.h" +#include "rtc_tools/data_channel_benchmark/signaling_interface.h" + +namespace webrtc { + +// Handles all the details for creating a PeerConnection and negotiation using a +// SignalingInterface object. +class PeerConnectionClient : public webrtc::PeerConnectionObserver { + public: + explicit PeerConnectionClient(webrtc::PeerConnectionFactoryInterface* factory, + webrtc::SignalingInterface* signaling); + + ~PeerConnectionClient() override; + + PeerConnectionClient(const PeerConnectionClient&) = delete; + PeerConnectionClient& operator=(const PeerConnectionClient&) = delete; + + // Set the local description and send offer using the SignalingInterface, + // initiating the negotiation process. + bool StartPeerConnection(); + + // Whether the peer connection is connected to the remote peer. + bool IsConnected(); + + // Disconnect from the call. + void Disconnect(); + + rtc::scoped_refptr peerConnection() { + return peer_connection_; + } + + // Set a callback to run when a DataChannel is created by the remote peer. + void SetOnDataChannel( + std::function)> + callback); + + std::vector>& + dataChannels() { + return data_channels_; + } + + // Creates a default PeerConnectionFactory object. + static rtc::scoped_refptr + CreateDefaultFactory(rtc::Thread* signaling_thread); + + private: + void AddIceCandidate( + std::unique_ptr candidate); + bool SetRemoteDescription( + std::unique_ptr desc); + + // Initialize the PeerConnection with a given PeerConnectionFactory. + bool InitializePeerConnection( + webrtc::PeerConnectionFactoryInterface* factory); + void DeletePeerConnection(); + + // PeerConnectionObserver implementation. + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override { + RTC_LOG(LS_INFO) << __FUNCTION__ << " new state: " << new_state; + } + void OnDataChannel( + rtc::scoped_refptr channel) override; + void OnNegotiationNeededEvent(uint32_t event_id) override; + void OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) override; + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override; + void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; + void OnIceConnectionReceivingChange(bool receiving) override { + RTC_LOG(LS_INFO) << __FUNCTION__ << " receiving? " << receiving; + } + + rtc::scoped_refptr peer_connection_; + std::function)> + on_data_channel_callback_; + std::vector> data_channels_; + webrtc::SignalingInterface* signaling_; +}; + +} // namespace webrtc + +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_PEER_CONNECTION_CLIENT_H_ diff --git a/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto b/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto new file mode 100644 index 0000000000..9bd0aae912 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/peer_connection_signaling.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package webrtc.GrpcSignaling; + +service PeerConnectionSignaling { + rpc Connect(stream SignalingMessage) returns (stream SignalingMessage) {} +} + +message SignalingMessage { + oneof Content { + SessionDescription description = 1; + IceCandidate candidate = 2; + } +} + +message SessionDescription { + enum SessionDescriptionType { + OFFER = 0; + ANSWER = 1; + } + SessionDescriptionType type = 1; + string content = 2; +} + +message IceCandidate { + string mid = 1; + int32 mline_index = 2; + string description = 3; +} \ No newline at end of file diff --git a/rtc_tools/data_channel_benchmark/signaling_interface.h b/rtc_tools/data_channel_benchmark/signaling_interface.h new file mode 100644 index 0000000000..77c811acb3 --- /dev/null +++ b/rtc_tools/data_channel_benchmark/signaling_interface.h @@ -0,0 +1,42 @@ +/* + * 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 RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ +#define RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ + +#include + +#include "api/jsep.h" + +namespace webrtc { +class SignalingInterface { + public: + virtual ~SignalingInterface() = default; + + // Send an ICE candidate over the transport. + virtual void SendIceCandidate( + const webrtc::IceCandidateInterface* candidate) = 0; + + // Send a local description over the transport. + virtual void SendDescription( + const webrtc::SessionDescriptionInterface* sdp) = 0; + + // Set a callback when receiving a description from the transport. + virtual void OnRemoteDescription( + std::function + sdp)> callback) = 0; + + // Set a callback when receiving an ICE candidate from the transport. + virtual void OnIceCandidate( + std::function + candidate)> callback) = 0; +}; +} // namespace webrtc + +#endif // RTC_TOOLS_DATA_CHANNEL_BENCHMARK_SIGNALING_INTERFACE_H_ diff --git a/rtc_tools/network_tester/BUILD.gn b/rtc_tools/network_tester/BUILD.gn index f7982d3eef..4fef8403c2 100644 --- a/rtc_tools/network_tester/BUILD.gn +++ b/rtc_tools/network_tester/BUILD.gn @@ -42,16 +42,17 @@ if (rtc_enable_protobuf) { "../../api:sequence_checker", "../../api/task_queue", "../../api/task_queue:default_task_queue_factory", - "../../p2p", + "../../p2p:rtc_p2p", "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:ignore_wundef", "../../rtc_base:ip_address", + "../../rtc_base:macromagic", "../../rtc_base:protobuf_utils", - "../../rtc_base:rtc_base_approved", "../../rtc_base:rtc_task_queue", "../../rtc_base:socket_address", "../../rtc_base:threading", + "../../rtc_base:timeutils", "../../rtc_base/synchronization:mutex", "../../rtc_base/system:no_unique_address", "../../rtc_base/third_party/sigslot", diff --git a/rtc_tools/rtc_event_log_visualizer/alerts.cc b/rtc_tools/rtc_event_log_visualizer/alerts.cc index 2d1868fa28..9ef5e9a77a 100644 --- a/rtc_tools/rtc_event_log_visualizer/alerts.cc +++ b/rtc_tools/rtc_event_log_visualizer/alerts.cc @@ -19,7 +19,6 @@ #include "logging/rtc_event_log/rtc_event_processor.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/sequence_number_util.h" #include "rtc_base/strings/string_builder.h" @@ -79,7 +78,7 @@ void TriageHelper::AnalyzeStreamGaps(const ParsedRtcEventLog& parsed_log, int64_t seq_num = seq_num_unwrapper.Unwrap(packet.header.sequenceNumber); if (std::abs(seq_num - last_seq_num) > kMaxSeqNumJump) { - Alert(seq_num_alert, config_.GetCallTimeSec(packet.log_time_us()), + Alert(seq_num_alert, config_.GetCallTimeSec(packet.log_time()), seq_num_explanation); } last_seq_num = seq_num; @@ -89,7 +88,7 @@ void TriageHelper::AnalyzeStreamGaps(const ParsedRtcEventLog& parsed_log, if (std::abs(capture_time - last_capture_time) > kTicksPerMillisec * (kCaptureTimeGraceMs + packet.log_time_ms() - last_log_time_ms)) { - Alert(capture_time_alert, config_.GetCallTimeSec(packet.log_time_us()), + Alert(capture_time_alert, config_.GetCallTimeSec(packet.log_time()), capture_time_explanation); } last_capture_time = capture_time; @@ -140,7 +139,8 @@ void TriageHelper::AnalyzeTransmissionGaps(const ParsedRtcEventLog& parsed_log, int64_t duration = timestamp - last_rtp_time.value_or(0); if (last_rtp_time.has_value() && duration > kMaxRtpTransmissionGap) { // No packet sent/received for more than 500 ms. - Alert(rtp_alert, config_.GetCallTimeSec(timestamp), rtp_explanation); + Alert(rtp_alert, config_.GetCallTimeSec(Timestamp::Micros(timestamp)), + rtp_explanation); } last_rtp_time.emplace(timestamp); } @@ -155,7 +155,7 @@ void TriageHelper::AnalyzeTransmissionGaps(const ParsedRtcEventLog& parsed_log, int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0); if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) { // No feedback sent/received for more than 2000 ms. - Alert(rtcp_alert, config_.GetCallTimeSec(rtcp.log_time_us()), + Alert(rtcp_alert, config_.GetCallTimeSec(rtcp.log_time()), rtcp_explanation); } last_rtcp_time.emplace(rtcp.log_time_us()); @@ -169,7 +169,7 @@ void TriageHelper::AnalyzeTransmissionGaps(const ParsedRtcEventLog& parsed_log, int64_t duration = rtcp.log_time_us() - last_rtcp_time.value_or(0); if (last_rtcp_time.has_value() && duration > kMaxRtcpTransmissionGap) { // No feedback sent/received for more than 2000 ms. - Alert(rtcp_alert, config_.GetCallTimeSec(rtcp.log_time_us()), + Alert(rtcp_alert, config_.GetCallTimeSec(rtcp.log_time()), rtcp_explanation); } last_rtcp_time.emplace(rtcp.log_time_us()); @@ -189,7 +189,7 @@ void TriageHelper::AnalyzeLog(const ParsedRtcEventLog& parsed_log) { const int64_t segment_end_us = parsed_log.first_log_segment().stop_time_us(); - int64_t first_occurrence = parsed_log.last_timestamp(); + Timestamp first_occurrence = parsed_log.last_timestamp(); constexpr double kMaxLossFraction = 0.05; // Loss feedback int64_t total_lost_packets = 0; @@ -204,13 +204,14 @@ void TriageHelper::AnalyzeLog(const ParsedRtcEventLog& parsed_log) { total_lost_packets += lost_packets; total_expected_packets += bwe_update.expected_packets; if (bwe_update.fraction_lost >= 255 * kMaxLossFraction) { - first_occurrence = std::min(first_occurrence, bwe_update.log_time_us()); + first_occurrence = std::min(first_occurrence, bwe_update.log_time()); } } double avg_outgoing_loss = static_cast(total_lost_packets) / total_expected_packets; if (avg_outgoing_loss > kMaxLossFraction) { - Alert(TriageAlertType::kOutgoingHighLoss, first_occurrence, + Alert(TriageAlertType::kOutgoingHighLoss, + config_.GetCallTimeSec(first_occurrence), "More than 5% of outgoing packets lost."); } } diff --git a/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc b/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc index 69a753bf22..854c6d05ae 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyze_audio.cc @@ -38,7 +38,7 @@ void CreateAudioEncoderTargetBitrateGraph(const ParsedRtcEventLog& parsed_log, return absl::nullopt; }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaBitrateBps, @@ -63,7 +63,7 @@ void CreateAudioEncoderFrameLengthGraph(const ParsedRtcEventLog& parsed_log, return absl::optional(); }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaFrameLengthMs, @@ -88,7 +88,7 @@ void CreateAudioEncoderPacketLossGraph(const ParsedRtcEventLog& parsed_log, return absl::optional(); }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaPacketLoss, @@ -114,7 +114,7 @@ void CreateAudioEncoderEnableFecGraph(const ParsedRtcEventLog& parsed_log, return absl::optional(); }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaFecEnabled, @@ -139,7 +139,7 @@ void CreateAudioEncoderEnableDtxGraph(const ParsedRtcEventLog& parsed_log, return absl::optional(); }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaDtxEnabled, @@ -164,7 +164,7 @@ void CreateAudioEncoderNumChannelsGraph(const ParsedRtcEventLog& parsed_log, return absl::optional(); }; auto ToCallTime = [config](const LoggedAudioNetworkAdaptationEvent& packet) { - return config.GetCallTimeSec(packet.log_time_us()); + return config.GetCallTimeSec(packet.log_time()); }; ProcessPoints( ToCallTime, GetAnaNumChannels, @@ -397,23 +397,23 @@ void CreateAudioJitterBufferGraph(const ParsedRtcEventLog& parsed_log, PointStyle::kHighlight); for (const auto& data : arrival_delay_ms) { - const float x = config.GetCallTimeSec(data.first * 1000); // ms to us. + const float x = config.GetCallTimeSec(Timestamp::Millis(data.first)); const float y = data.second; time_series_packet_arrival.points.emplace_back(TimeSeriesPoint(x, y)); } for (const auto& data : corrected_arrival_delay_ms) { - const float x = config.GetCallTimeSec(data.first * 1000); // ms to us. + const float x = config.GetCallTimeSec(Timestamp::Millis(data.first)); const float y = data.second; time_series_relative_packet_arrival.points.emplace_back( TimeSeriesPoint(x, y)); } for (const auto& data : playout_delay_ms) { - const float x = config.GetCallTimeSec(data.first * 1000); // ms to us. + const float x = config.GetCallTimeSec(Timestamp::Millis(data.first)); const float y = data.second; time_series_play_time.points.emplace_back(TimeSeriesPoint(x, y)); } for (const auto& data : target_delay_ms) { - const float x = config.GetCallTimeSec(data.first * 1000); // ms to us. + const float x = config.GetCallTimeSec(Timestamp::Millis(data.first)); const float y = data.second; time_series_target_time.points.emplace_back(TimeSeriesPoint(x, y)); } @@ -448,7 +448,7 @@ void CreateNetEqStatsGraphInternal( const std::vector>* data_vector = data_extractor(st.second.get()); for (const auto& data : *data_vector) { - const float time = config.GetCallTimeSec(data.first * 1000); // ms to us. + const float time = config.GetCallTimeSec(Timestamp::Millis(data.first)); const float value = stats_extractor(data.second); time_series[ssrc].points.emplace_back(TimeSeriesPoint(time, value)); } diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc index b43801623c..f94adc28f6 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc @@ -38,8 +38,6 @@ #include "modules/congestion_controller/goog_cc/delay_based_bwe.h" #include "modules/congestion_controller/include/receive_side_congestion_controller.h" #include "modules/congestion_controller/rtp/transport_feedback_adapter.h" -#include "modules/pacing/paced_sender.h" -#include "modules/pacing/packet_router.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/common_header.h" @@ -50,7 +48,6 @@ #include "modules/rtp_rtcp/source/rtp_header_extensions.h" #include "modules/rtp_rtcp/source/rtp_rtcp_interface.h" #include "rtc_base/checks.h" -#include "rtc_base/format_macros.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/sequence_number_util.h" #include "rtc_base/rate_statistics.h" @@ -214,7 +211,7 @@ TimeSeries CreateRtcpTypeTimeSeries(const std::vector& rtcp_list, int category_id) { TimeSeries time_series(rtcp_name, LineStyle::kNone, PointStyle::kHighlight); for (const auto& rtcp : rtcp_list) { - float x = config.GetCallTimeSec(rtcp.log_time_us()); + float x = config.GetCallTimeSec(rtcp.timestamp); float y = category_id; time_series.points.emplace_back(x, y); } @@ -346,20 +343,24 @@ std::string GetDirectionAsShortString(PacketDirection direction) { EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log, bool normalize_time) : parsed_log_(log) { - config_.window_duration_ = 250000; - config_.step_ = 10000; + config_.window_duration_ = TimeDelta::Millis(250); + config_.step_ = TimeDelta::Millis(10); + if (!log.start_log_events().empty()) { + config_.rtc_to_utc_offset_ = log.start_log_events()[0].utc_time() - + log.start_log_events()[0].log_time(); + } config_.normalize_time_ = normalize_time; config_.begin_time_ = parsed_log_.first_timestamp(); config_.end_time_ = parsed_log_.last_timestamp(); if (config_.end_time_ < config_.begin_time_) { RTC_LOG(LS_WARNING) << "No useful events in the log."; - config_.begin_time_ = config_.end_time_ = 0; + config_.begin_time_ = config_.end_time_ = Timestamp::Zero(); } RTC_LOG(LS_INFO) << "Log is " - << (parsed_log_.last_timestamp() - - parsed_log_.first_timestamp()) / - 1000000 + << (parsed_log_.last_timestamp().ms() - + parsed_log_.first_timestamp().ms()) / + 1000 << " seconds long."; } @@ -367,9 +368,9 @@ EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log, const AnalyzerConfig& config) : parsed_log_(log), config_(config) { RTC_LOG(LS_INFO) << "Log is " - << (parsed_log_.last_timestamp() - - parsed_log_.first_timestamp()) / - 1000000 + << (parsed_log_.last_timestamp().ms() - + parsed_log_.first_timestamp().ms()) / + 1000 << " seconds long."; } @@ -413,7 +414,7 @@ void EventLogAnalyzer::CreatePacketGraph(PacketDirection direction, return absl::optional(packet.total_length); }; auto ToCallTime = [this](const LoggedRtpPacket& packet) { - return this->config_.GetCallTimeSec(packet.log_time_us()); + return this->config_.GetCallTimeSec(packet.timestamp); }; ProcessPoints(ToCallTime, GetPacketSize, stream.packet_view, &time_series); @@ -469,7 +470,7 @@ void EventLogAnalyzer::CreateAccumulatedPacketsTimeSeries( const std::string& label) { TimeSeries time_series(label, LineStyle::kStep); for (size_t i = 0; i < packets.size(); i++) { - float x = config_.GetCallTimeSec(packets[i].log_time_us()); + float x = config_.GetCallTimeSec(packets[i].log_time()); time_series.points.emplace_back(x, i + 1); } plot->AppendTimeSeries(std::move(time_series)); @@ -545,17 +546,15 @@ void EventLogAnalyzer::CreateTotalPacketRateGraph(PacketDirection direction, // types using MovingAverage(). class LogTime { public: - explicit LogTime(int64_t log_time_us) : log_time_us_(log_time_us) {} - - int64_t log_time_us() const { return log_time_us_; } + explicit LogTime(Timestamp log_time) : log_time_(log_time) {} + Timestamp log_time() const { return log_time_; } private: - int64_t log_time_us_; + Timestamp log_time_; }; - std::vector packet_times; auto handle_rtp = [&](const LoggedRtpPacket& packet) { - packet_times.emplace_back(packet.log_time_us()); + packet_times.emplace_back(packet.log_time()); }; RtcEventProcessor process; for (const auto& stream : parsed_log_.rtp_packets_by_ssrc(direction)) { @@ -563,13 +562,13 @@ void EventLogAnalyzer::CreateTotalPacketRateGraph(PacketDirection direction, } if (direction == kIncomingPacket) { auto handle_incoming_rtcp = [&](const LoggedRtcpPacketIncoming& packet) { - packet_times.emplace_back(packet.log_time_us()); + packet_times.emplace_back(packet.log_time()); }; process.AddEvents(parsed_log_.incoming_rtcp_packets(), handle_incoming_rtcp); } else { auto handle_outgoing_rtcp = [&](const LoggedRtcpPacketOutgoing& packet) { - packet_times.emplace_back(packet.log_time_us()); + packet_times.emplace_back(packet.log_time()); }; process.AddEvents(parsed_log_.outgoing_rtcp_packets(), handle_outgoing_rtcp); @@ -599,7 +598,7 @@ void EventLogAnalyzer::CreatePlayoutGraph(Plot* plot) { absl::optional last_playout_ms; TimeSeries time_series(SsrcToString(ssrc), LineStyle::kBar); for (const auto& playout_event : playout_stream.second) { - float x = config_.GetCallTimeSec(playout_event.log_time_us()); + float x = config_.GetCallTimeSec(playout_event.log_time()); int64_t playout_time_ms = playout_event.log_time_ms(); // If there were no previous playouts, place the point on the x-axis. float y = playout_time_ms - last_playout_ms.value_or(playout_time_ms); @@ -626,7 +625,7 @@ void EventLogAnalyzer::CreateAudioLevelGraph(PacketDirection direction, LineStyle::kLine); for (auto& packet : stream.packet_view) { if (packet.header.extension.hasAudioLevel) { - float x = config_.GetCallTimeSec(packet.log_time_us()); + float x = config_.GetCallTimeSec(packet.log_time()); // The audio level is stored in -dBov (so e.g. -10 dBov is stored as 10) // Here we convert it to dBov. float y = static_cast(-packet.header.extension.audioLevel); @@ -662,7 +661,7 @@ void EventLogAnalyzer::CreateSequenceNumberGraph(Plot* plot) { return diff; }; auto ToCallTime = [this](const LoggedRtpPacketIncoming& packet) { - return this->config_.GetCallTimeSec(packet.log_time_us()); + return this->config_.GetCallTimeSec(packet.log_time()); }; ProcessPairs( ToCallTime, GetSequenceNumberDiff, stream.incoming_packets, @@ -691,8 +690,8 @@ void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) { LineStyle::kLine, PointStyle::kHighlight); // TODO(terelius): Should the window and step size be read from the class // instead? - const int64_t kWindowUs = 1000000; - const int64_t kStep = 1000000; + const TimeDelta kWindow = TimeDelta::Millis(1000); + const TimeDelta kStep = TimeDelta::Millis(1000); SeqNumUnwrapper unwrapper_; SeqNumUnwrapper prior_unwrapper_; size_t window_index_begin = 0; @@ -702,17 +701,17 @@ void EventLogAnalyzer::CreateIncomingPacketLossGraph(Plot* plot) { uint64_t highest_prior_seq_number = prior_unwrapper_.Unwrap(packets[0].rtp.header.sequenceNumber) - 1; - for (int64_t t = config_.begin_time_; t < config_.end_time_ + kStep; + for (Timestamp t = config_.begin_time_; t < config_.end_time_ + kStep; t += kStep) { while (window_index_end < packets.size() && - packets[window_index_end].rtp.log_time_us() < t) { + packets[window_index_end].rtp.log_time() < t) { uint64_t sequence_number = unwrapper_.Unwrap( packets[window_index_end].rtp.header.sequenceNumber); highest_seq_number = std::max(highest_seq_number, sequence_number); ++window_index_end; } while (window_index_begin < packets.size() && - packets[window_index_begin].rtp.log_time_us() < t - kWindowUs) { + packets[window_index_begin].rtp.log_time() < t - kWindow) { uint64_t sequence_number = prior_unwrapper_.Unwrap( packets[window_index_begin].rtp.header.sequenceNumber); highest_prior_seq_number = @@ -767,7 +766,7 @@ void EventLogAnalyzer::CreateIncomingDelayGraph(Plot* plot) { } auto ToCallTime = [this](const LoggedRtpPacketIncoming& packet) { - return this->config_.GetCallTimeSec(packet.log_time_us()); + return this->config_.GetCallTimeSec(packet.log_time()); }; auto ToNetworkDelay = [frequency_hz]( const LoggedRtpPacketIncoming& old_packet, @@ -803,7 +802,7 @@ void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) { TimeSeries time_series("Fraction lost", LineStyle::kLine, PointStyle::kHighlight); for (auto& bwe_update : parsed_log_.bwe_loss_updates()) { - float x = config_.GetCallTimeSec(bwe_update.log_time_us()); + float x = config_.GetCallTimeSec(bwe_update.log_time()); float y = static_cast(bwe_update.fraction_lost) / 255 * 100; time_series.points.emplace_back(x, y); } @@ -818,11 +817,11 @@ void EventLogAnalyzer::CreateFractionLossGraph(Plot* plot) { // Plot the total bandwidth used by all RTP streams. void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) { // TODO(terelius): This could be provided by the parser. - std::multimap packets_in_order; + std::multimap packets_in_order; for (const auto& stream : parsed_log_.incoming_rtp_packets_by_ssrc()) { for (const LoggedRtpPacketIncoming& packet : stream.incoming_packets) packets_in_order.insert( - std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length)); + std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); } auto window_begin = packets_in_order.begin(); @@ -832,7 +831,7 @@ void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) { if (!packets_in_order.empty()) { // Calculate a moving average of the bitrate and store in a TimeSeries. TimeSeries bitrate_series("Bitrate", LineStyle::kLine); - for (int64_t time = config_.begin_time_; + for (Timestamp time = config_.begin_time_; time < config_.end_time_ + config_.step_; time += config_.step_) { while (window_end != packets_in_order.end() && window_end->first < time) { bytes_in_window += window_end->second; @@ -845,7 +844,8 @@ void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) { ++window_begin; } float window_duration_in_seconds = - static_cast(config_.window_duration_) / kNumMicrosecsPerSec; + static_cast(config_.window_duration_.us()) / + kNumMicrosecsPerSec; float x = config_.GetCallTimeSec(time); float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; bitrate_series.points.emplace_back(x, y); @@ -856,7 +856,7 @@ void EventLogAnalyzer::CreateTotalIncomingBitrateGraph(Plot* plot) { // Overlay the outgoing REMB over incoming bitrate. TimeSeries remb_series("Remb", LineStyle::kStep); for (const auto& rtcp : parsed_log_.rembs(kOutgoingPacket)) { - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; remb_series.points.emplace_back(x, y); } @@ -884,11 +884,11 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, bool show_detector_state, bool show_alr_state) { // TODO(terelius): This could be provided by the parser. - std::multimap packets_in_order; + std::multimap packets_in_order; for (const auto& stream : parsed_log_.outgoing_rtp_packets_by_ssrc()) { for (const LoggedRtpPacketOutgoing& packet : stream.outgoing_packets) packets_in_order.insert( - std::make_pair(packet.rtp.log_time_us(), packet.rtp.total_length)); + std::make_pair(packet.rtp.log_time(), packet.rtp.total_length)); } auto window_begin = packets_in_order.begin(); @@ -898,7 +898,7 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, if (!packets_in_order.empty()) { // Calculate a moving average of the bitrate and store in a TimeSeries. TimeSeries bitrate_series("Bitrate", LineStyle::kLine); - for (int64_t time = config_.begin_time_; + for (Timestamp time = config_.begin_time_; time < config_.end_time_ + config_.step_; time += config_.step_) { while (window_end != packets_in_order.end() && window_end->first < time) { bytes_in_window += window_end->second; @@ -911,7 +911,8 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, ++window_begin; } float window_duration_in_seconds = - static_cast(config_.window_duration_) / kNumMicrosecsPerSec; + static_cast(config_.window_duration_.us()) / + kNumMicrosecsPerSec; float x = config_.GetCallTimeSec(time); float y = bytes_in_window * 8 / window_duration_in_seconds / 1000; bitrate_series.points.emplace_back(x, y); @@ -922,7 +923,7 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, // Overlay the send-side bandwidth estimate over the outgoing bitrate. TimeSeries loss_series("Loss-based estimate", LineStyle::kStep); for (auto& loss_update : parsed_log_.bwe_loss_updates()) { - float x = config_.GetCallTimeSec(loss_update.log_time_us()); + float x = config_.GetCallTimeSec(loss_update.log_time()); float y = static_cast(loss_update.bitrate_bps) / 1000; loss_series.points.emplace_back(x, y); } @@ -935,12 +936,12 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, IntervalSeries normal_series("Normal", "#c4ffc4", IntervalSeries::kHorizontal); IntervalSeries* last_series = &normal_series; - double last_detector_switch = 0.0; + float last_detector_switch = 0.0; BandwidthUsage last_detector_state = BandwidthUsage::kBwNormal; for (auto& delay_update : parsed_log_.bwe_delay_updates()) { - float x = config_.GetCallTimeSec(delay_update.log_time_us()); + float x = config_.GetCallTimeSec(delay_update.log_time()); float y = static_cast(delay_update.bitrate_bps) / 1000; if (last_detector_state != delay_update.detector_state) { @@ -967,12 +968,13 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, } RTC_CHECK(last_series); - last_series->intervals.emplace_back(last_detector_switch, config_.end_time_); + last_series->intervals.emplace_back(last_detector_switch, + config_.CallEndTimeSec()); TimeSeries created_series("Probe cluster created.", LineStyle::kNone, PointStyle::kHighlight); for (auto& cluster : parsed_log_.bwe_probe_cluster_created_events()) { - float x = config_.GetCallTimeSec(cluster.log_time_us()); + float x = config_.GetCallTimeSec(cluster.log_time()); float y = static_cast(cluster.bitrate_bps) / 1000; created_series.points.emplace_back(x, y); } @@ -980,7 +982,7 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, TimeSeries result_series("Probing results.", LineStyle::kNone, PointStyle::kHighlight); for (auto& result : parsed_log_.bwe_probe_success_events()) { - float x = config_.GetCallTimeSec(result.log_time_us()); + float x = config_.GetCallTimeSec(result.log_time()); float y = static_cast(result.bitrate_bps) / 1000; result_series.points.emplace_back(x, y); } @@ -988,17 +990,17 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, TimeSeries probe_failures_series("Probe failed", LineStyle::kNone, PointStyle::kHighlight); for (auto& failure : parsed_log_.bwe_probe_failure_events()) { - float x = config_.GetCallTimeSec(failure.log_time_us()); + float x = config_.GetCallTimeSec(failure.log_time()); probe_failures_series.points.emplace_back(x, 0); } IntervalSeries alr_state("ALR", "#555555", IntervalSeries::kHorizontal); bool previously_in_alr = false; - int64_t alr_start = 0; + Timestamp alr_start = Timestamp::Zero(); for (auto& alr : parsed_log_.alr_state_events()) { - float y = config_.GetCallTimeSec(alr.log_time_us()); + float y = config_.GetCallTimeSec(alr.log_time()); if (!previously_in_alr && alr.in_alr) { - alr_start = alr.log_time_us(); + alr_start = alr.log_time(); previously_in_alr = true; } else if (previously_in_alr && !alr.in_alr) { float x = config_.GetCallTimeSec(alr_start); @@ -1031,7 +1033,7 @@ void EventLogAnalyzer::CreateTotalOutgoingBitrateGraph(Plot* plot, // Overlay the incoming REMB over the outgoing bitrate. TimeSeries remb_series("Remb", LineStyle::kStep); for (const auto& rtcp : parsed_log_.rembs(kIncomingPacket)) { - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); float y = static_cast(rtcp.remb.bitrate_bps()) / 1000; remb_series.points.emplace_back(x, y); } @@ -1117,7 +1119,7 @@ void EventLogAnalyzer::CreateBitrateAllocationGraph(PacketDirection direction, std::make_pair(layer, TimeSeries(layer_name, LineStyle::kStep))); RTC_DCHECK(inserted); } - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); float y = bitrate_item.target_bitrate_kbps; time_series_it->second.points.emplace_back(x, y); } @@ -1149,22 +1151,20 @@ void EventLogAnalyzer::CreateGoogCcSimulationGraph(Plot* plot) { [&](const NetworkControlUpdate& update, Timestamp at_time) { if (update.target_rate) { target_rates.points.emplace_back( - config_.GetCallTimeSec(at_time.us()), + config_.GetCallTimeSec(at_time), update.target_rate->target_rate.kbps()); } }); simulation.ProcessEventsInLog(parsed_log_); for (const auto& logged : parsed_log_.bwe_delay_updates()) - delay_based.points.emplace_back( - config_.GetCallTimeSec(logged.log_time_us()), - logged.bitrate_bps / 1000); + delay_based.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), + logged.bitrate_bps / 1000); for (const auto& logged : parsed_log_.bwe_probe_success_events()) - probe_results.points.emplace_back( - config_.GetCallTimeSec(logged.log_time_us()), - logged.bitrate_bps / 1000); + probe_results.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), + logged.bitrate_bps / 1000); for (const auto& logged : parsed_log_.bwe_loss_updates()) - loss_based.points.emplace_back(config_.GetCallTimeSec(logged.log_time_us()), + loss_based.points.emplace_back(config_.GetCallTimeSec(logged.log_time()), logged.bitrate_bps / 1000); plot->AppendTimeSeries(std::move(delay_based)); @@ -1196,8 +1196,6 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { SimulatedClock clock(0); BitrateObserver observer; RtcEventLogNull null_event_log; - PacketRouter packet_router; - PacedSender pacer(&clock, &packet_router, &null_event_log); TransportFeedbackAdapter transport_feedback; auto factory = GoogCcNetworkControllerFactory(); TimeDelta process_interval = factory.GetProcessInterval(); @@ -1244,11 +1242,11 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { return std::numeric_limits::max(); }; - RateStatistics acked_bitrate(750, 8000); + RateStatistics raw_acked_bitrate(750, 8000); test::ExplicitKeyValueConfig throughput_config( "WebRTC-Bwe-RobustThroughputEstimatorSettings/" - "enabled:true,reduce_bias:true,assume_shared_link:false,initial_packets:" - "10,min_packets:25,window_duration:750ms,unacked_weight:0.5/"); + "enabled:true,required_packets:10," + "window_packets:25,window_duration:1000ms,unacked_weight:1.0/"); std::unique_ptr robust_throughput_estimator( AcknowledgedBitrateEstimatorInterface::Create(&throughput_config)); @@ -1307,7 +1305,6 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { auto feedback_msg = transport_feedback.ProcessTransportFeedback( rtcp_iterator->transport_feedback, Timestamp::Millis(clock.TimeInMilliseconds())); - absl::optional bitrate_bps; if (feedback_msg) { observer.Update(goog_cc->OnTransportPacketsFeedback(*feedback_msg)); std::vector feedback = @@ -1317,24 +1314,30 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { feedback); robust_throughput_estimator->IncomingPacketFeedbackVector(feedback); for (const PacketResult& packet : feedback) { - acked_bitrate.Update(packet.sent_packet.size.bytes(), - packet.receive_time.ms()); + raw_acked_bitrate.Update(packet.sent_packet.size.bytes(), + packet.receive_time.ms()); + } + absl::optional raw_bitrate_bps = + raw_acked_bitrate.Rate(feedback.back().receive_time.ms()); + float x = config_.GetCallTimeSec(clock.CurrentTime()); + if (raw_bitrate_bps) { + float y = raw_bitrate_bps.value() / 1000; + acked_time_series.points.emplace_back(x, y); + } + absl::optional robust_estimate = + robust_throughput_estimator->bitrate(); + if (robust_estimate) { + float y = robust_estimate.value().kbps(); + robust_time_series.points.emplace_back(x, y); + } + absl::optional acked_estimate = + acknowledged_bitrate_estimator->bitrate(); + if (acked_estimate) { + float y = acked_estimate.value().kbps(); + acked_estimate_time_series.points.emplace_back(x, y); } - bitrate_bps = acked_bitrate.Rate(feedback.back().receive_time.ms()); } } - - float x = config_.GetCallTimeSec(clock.TimeInMicroseconds()); - float y = bitrate_bps.value_or(0) / 1000; - acked_time_series.points.emplace_back(x, y); - y = robust_throughput_estimator->bitrate() - .value_or(DataRate::Zero()) - .kbps(); - robust_time_series.points.emplace_back(x, y); - y = acknowledged_bitrate_estimator->bitrate() - .value_or(DataRate::Zero()) - .kbps(); - acked_estimate_time_series.points.emplace_back(x, y); ++rtcp_iterator; } if (clock.TimeInMicroseconds() >= NextProcessTime()) { @@ -1347,7 +1350,7 @@ void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { if (observer.GetAndResetBitrateUpdated() || time_us - last_update_us >= 1e6) { uint32_t y = observer.last_bitrate_bps() / 1000; - float x = config_.GetCallTimeSec(clock.TimeInMicroseconds()); + float x = config_.GetCallTimeSec(clock.CurrentTime()); time_series.points.emplace_back(x, y); last_update_us = time_us; } @@ -1423,13 +1426,13 @@ void EventLogAnalyzer::CreateReceiveSideBweSimulationGraph(Plot* plot) { absl::optional bitrate_bps = acked_bitrate.Rate(arrival_time_ms); if (bitrate_bps) { uint32_t y = *bitrate_bps / 1000; - float x = config_.GetCallTimeSec(clock.TimeInMicroseconds()); + float x = config_.GetCallTimeSec(clock.CurrentTime()); acked_time_series.points.emplace_back(x, y); } if (remb_interceptor.GetAndResetBitrateUpdated() || clock.TimeInMicroseconds() - last_update_us >= 1e6) { uint32_t y = remb_interceptor.last_bitrate_bps() / 1000; - float x = config_.GetCallTimeSec(clock.TimeInMicroseconds()); + float x = config_.GetCallTimeSec(clock.CurrentTime()); time_series.points.emplace_back(x, y); last_update_us = clock.TimeInMicroseconds(); } @@ -1461,7 +1464,7 @@ void EventLogAnalyzer::CreateNetworkDelayFeedbackGraph(Plot* plot) { for (const auto& packet : matched_rtp_rtcp) { if (packet.arrival_time_ms == MatchedSendArrivalTimes::kNotReceived) continue; - float x = config_.GetCallTimeSec(1000 * packet.feedback_arrival_time_ms); + float x = config_.GetCallTimeSecFromMs(packet.feedback_arrival_time_ms); int64_t y = packet.arrival_time_ms - packet.send_time_ms; int64_t rtt_ms = packet.feedback_arrival_time_ms - packet.send_time_ms; min_rtt_ms = std::min(rtt_ms, min_rtt_ms); @@ -1530,7 +1533,7 @@ void EventLogAnalyzer::CreatePacerDelayGraph(Plot* plot) { double send_time_ms = static_cast(packet.rtp.log_time_us() - first_send_timestamp) / 1000; - float x = config_.GetCallTimeSec(packet.rtp.log_time_us()); + float x = config_.GetCallTimeSec(packet.rtp.log_time()); float y = send_time_ms - capture_time_ms; pacer_delay_series.points.emplace_back(x, y); } @@ -1551,7 +1554,7 @@ void EventLogAnalyzer::CreateTimestampGraph(PacketDirection direction, GetStreamName(parsed_log_, direction, stream.ssrc) + " capture-time", LineStyle::kLine, PointStyle::kHighlight); for (const auto& packet : stream.packet_view) { - float x = config_.GetCallTimeSec(packet.log_time_us()); + float x = config_.GetCallTimeSec(packet.log_time()); float y = packet.header.timestamp; rtp_timestamps.points.emplace_back(x, y); } @@ -1566,7 +1569,7 @@ void EventLogAnalyzer::CreateTimestampGraph(PacketDirection direction, for (const auto& rtcp : sender_reports) { if (rtcp.sr.sender_ssrc() != stream.ssrc) continue; - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); float y = rtcp.sr.rtp_timestamp(); rtcp_timestamps.points.emplace_back(x, y); } @@ -1588,7 +1591,7 @@ void EventLogAnalyzer::CreateSenderAndReceiverReportPlot( std::map sr_reports_by_ssrc; const auto& sender_reports = parsed_log_.sender_reports(direction); for (const auto& rtcp : sender_reports) { - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); uint32_t ssrc = rtcp.sr.sender_ssrc(); for (const auto& block : rtcp.sr.report_blocks()) { float y = fy(block); @@ -1610,7 +1613,7 @@ void EventLogAnalyzer::CreateSenderAndReceiverReportPlot( std::map rr_reports_by_ssrc; const auto& receiver_reports = parsed_log_.receiver_reports(direction); for (const auto& rtcp : receiver_reports) { - float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float x = config_.GetCallTimeSec(rtcp.log_time()); uint32_t ssrc = rtcp.rr.sender_ssrc(); for (const auto& block : rtcp.rr.report_blocks()) { float y = fy(block); @@ -1649,7 +1652,7 @@ void EventLogAnalyzer::CreateIceCandidatePairConfigGraph(Plot* plot) { candidate_pair_desc_by_id_[config.candidate_pair_id] = candidate_pair_desc; } - float x = config_.GetCallTimeSec(config.log_time_us()); + float x = config_.GetCallTimeSec(config.log_time()); float y = static_cast(config.type); configs_by_cp_id[config.candidate_pair_id].points.emplace_back(x, y); } @@ -1706,7 +1709,7 @@ void EventLogAnalyzer::CreateIceConnectivityCheckGraph(Plot* plot) { GetCandidatePairLogDescriptionFromId(event.candidate_pair_id), LineStyle::kNone, PointStyle::kHighlight); } - float x = config_.GetCallTimeSec(event.log_time_us()); + float x = config_.GetCallTimeSec(event.log_time()); float y = static_cast(event.type) + kEventTypeOffset; checks_by_cp_id[event.candidate_pair_id].points.emplace_back(x, y); } @@ -1741,7 +1744,7 @@ void EventLogAnalyzer::CreateDtlsTransportStateGraph(Plot* plot) { TimeSeries states("DTLS Transport State", LineStyle::kNone, PointStyle::kHighlight); for (const auto& event : parsed_log_.dtls_transport_states()) { - float x = config_.GetCallTimeSec(event.log_time_us()); + float x = config_.GetCallTimeSec(event.log_time()); float y = static_cast(event.dtls_transport_state); states.points.emplace_back(x, y); } @@ -1763,7 +1766,7 @@ void EventLogAnalyzer::CreateDtlsWritableStateGraph(Plot* plot) { TimeSeries writable("DTLS Writable", LineStyle::kNone, PointStyle::kHighlight); for (const auto& event : parsed_log_.dtls_writable_states()) { - float x = config_.GetCallTimeSec(event.log_time_us()); + float x = config_.GetCallTimeSec(event.log_time()); float y = static_cast(event.writable); writable.points.emplace_back(x, y); } diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer_common.h b/rtc_tools/rtc_event_log_visualizer/analyzer_common.h index a4305a708f..b0b556aa62 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer_common.h +++ b/rtc_tools/rtc_event_log_visualizer/analyzer_common.h @@ -22,6 +22,7 @@ namespace webrtc { constexpr int kNumMicrosecsPerSec = 1000000; +constexpr int kNumMillisecsPerSec = 1000; constexpr float kLeftMargin = 0.01f; constexpr float kRightMargin = 0.02f; constexpr float kBottomMargin = 0.02f; @@ -29,25 +30,38 @@ constexpr float kTopMargin = 0.05f; class AnalyzerConfig { public: - float GetCallTimeSec(int64_t timestamp_us) const { - int64_t offset = normalize_time_ ? begin_time_ : 0; - return static_cast(timestamp_us - offset) / 1000000; + float GetCallTimeSec(Timestamp timestamp) const { + Timestamp offset = normalize_time_ ? begin_time_ : Timestamp::Zero(); + return static_cast((timestamp - offset).us()) / 1000000; + } + + float GetCallTimeSecFromMs(int64_t timestamp_ms) const { + return GetCallTimeSec(Timestamp::Millis(timestamp_ms)); } float CallBeginTimeSec() const { return GetCallTimeSec(begin_time_); } float CallEndTimeSec() const { return GetCallTimeSec(end_time_); } + int64_t CallTimeToUtcOffsetMs() { + if (normalize_time_) { + Timestamp utc_begin_time_ = begin_time_ + rtc_to_utc_offset_; + return utc_begin_time_.ms(); + } + return rtc_to_utc_offset_.ms(); + } + // Window and step size used for calculating moving averages, e.g. bitrate. - // The generated data points will be `step_` microseconds apart. - // Only events occurring at most `window_duration_` microseconds before the - // current data point will be part of the average. - int64_t window_duration_; - int64_t step_; + // The generated data points will be `step_.ms()` milliseconds apart. + // Only events occurring at most `window_duration_.ms()` milliseconds before + // the current data point will be part of the average. + TimeDelta window_duration_ = TimeDelta::Millis(250); + TimeDelta step_ = TimeDelta::Millis(10); // First and last events of the log. - int64_t begin_time_; - int64_t end_time_; + Timestamp begin_time_ = Timestamp::MinusInfinity(); + Timestamp end_time_ = Timestamp::MinusInfinity(); + TimeDelta rtc_to_utc_offset_ = TimeDelta::Zero(); bool normalize_time_; }; @@ -152,17 +166,17 @@ void MovingAverage( size_t window_index_end = 0; ResultType sum_in_window = 0; - for (int64_t t = config.begin_time_; t < config.end_time_ + config.step_; + for (Timestamp t = config.begin_time_; t < config.end_time_ + config.step_; t += config.step_) { while (window_index_end < data_view.size() && - data_view[window_index_end].log_time_us() < t) { + data_view[window_index_end].log_time() < t) { absl::optional value = fy(data_view[window_index_end]); if (value) sum_in_window += *value; ++window_index_end; } while (window_index_begin < data_view.size() && - data_view[window_index_begin].log_time_us() < + data_view[window_index_begin].log_time() < t - config.window_duration_) { absl::optional value = fy(data_view[window_index_begin]); if (value) @@ -170,7 +184,7 @@ void MovingAverage( ++window_index_begin; } float window_duration_s = - static_cast(config.window_duration_) / kNumMicrosecsPerSec; + static_cast(config.window_duration_.us()) / kNumMicrosecsPerSec; float x = config.GetCallTimeSec(t); float y = sum_in_window / window_duration_s; result->points.emplace_back(x, y); diff --git a/rtc_tools/rtc_event_log_visualizer/log_simulation.cc b/rtc_tools/rtc_event_log_visualizer/log_simulation.cc index c0b418de4b..30c4de199a 100644 --- a/rtc_tools/rtc_event_log_visualizer/log_simulation.cc +++ b/rtc_tools/rtc_event_log_visualizer/log_simulation.cc @@ -152,7 +152,7 @@ void LogBasedNetworkControllerSimulation::OnReceiverReport( CompactNtp(clock->ConvertTimestampToNtpTime(report_log_time)); uint32_t rtt_ntp = receive_time_ntp - rb.delay_since_last_sr() - rb.last_sr(); - rtt = std::min(rtt, TimeDelta::Millis(CompactNtpRttToMs(rtt_ntp))); + rtt = std::min(rtt, CompactNtpRttToTimeDelta(rtt_ntp)); } } if (rtt.IsFinite()) { diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc index ab4b7ebac1..6182b701df 100644 --- a/rtc_tools/rtc_event_log_visualizer/main.cc +++ b/rtc_tools/rtc_event_log_visualizer/main.cc @@ -261,8 +261,12 @@ int main(int argc, char* argv[]) { } webrtc::AnalyzerConfig config; - config.window_duration_ = 250000; - config.step_ = 10000; + config.window_duration_ = webrtc::TimeDelta::Millis(250); + config.step_ = webrtc::TimeDelta::Millis(10); + if (!parsed_log.start_log_events().empty()) { + config.rtc_to_utc_offset_ = parsed_log.start_log_events()[0].utc_time() - + parsed_log.start_log_events()[0].log_time(); + } config.normalize_time_ = absl::GetFlag(FLAGS_normalize_time); config.begin_time_ = parsed_log.first_timestamp(); config.end_time_ = parsed_log.last_timestamp(); @@ -275,6 +279,7 @@ int main(int argc, char* argv[]) { webrtc::EventLogAnalyzer analyzer(parsed_log, config); webrtc::PlotCollection collection; + collection.SetCallTimeToUtcOffsetMs(config.CallTimeToUtcOffsetMs()); PlotMap plots; plots.RegisterPlot("incoming_packet_sizes", [&](Plot* plot) { diff --git a/rtc_tools/rtc_event_log_visualizer/plot_base.cc b/rtc_tools/rtc_event_log_visualizer/plot_base.cc index 82533e6eb0..bf76a67bb5 100644 --- a/rtc_tools/rtc_event_log_visualizer/plot_base.cc +++ b/rtc_tools/rtc_event_log_visualizer/plot_base.cc @@ -307,13 +307,13 @@ void PlotCollection::PrintPythonCode(bool shared_xaxis) const { void PlotCollection::ExportProtobuf( webrtc::analytics::ChartCollection* collection) const { for (const auto& plot : plots_) { - // TODO(terelius): Ensure that there is no way to insert plots other than - // ProtobufPlots in a ProtobufPlotCollection. Needed to safely static_cast - // here. webrtc::analytics::Chart* protobuf_representation = collection->add_charts(); plot->ExportProtobuf(protobuf_representation); } + if (calltime_to_utc_ms_) { + collection->set_calltime_to_utc_ms(*calltime_to_utc_ms_); + } } Plot* PlotCollection::AppendNewPlot() { diff --git a/rtc_tools/rtc_event_log_visualizer/plot_base.h b/rtc_tools/rtc_event_log_visualizer/plot_base.h index a26146b5e5..dbc9535fc1 100644 --- a/rtc_tools/rtc_event_log_visualizer/plot_base.h +++ b/rtc_tools/rtc_event_log_visualizer/plot_base.h @@ -16,6 +16,7 @@ #include #include "absl/base/attributes.h" +#include "absl/types/optional.h" #include "rtc_base/ignore_wundef.h" RTC_PUSH_IGNORING_WUNDEF() @@ -194,6 +195,10 @@ class PlotCollection { virtual Plot* AppendNewPlot(); + void SetCallTimeToUtcOffsetMs(int64_t calltime_to_utc_ms) { + calltime_to_utc_ms_ = calltime_to_utc_ms; + } + // Replaces PythonPlotCollection::Draw() void PrintPythonCode(bool shared_xaxis) const; @@ -202,6 +207,7 @@ class PlotCollection { protected: std::vector> plots_; + absl::optional calltime_to_utc_ms_; }; } // namespace webrtc diff --git a/rtc_tools/rtc_event_log_visualizer/proto/chart.proto b/rtc_tools/rtc_event_log_visualizer/proto/chart.proto index e5960b2677..f82d9c1ead 100644 --- a/rtc_tools/rtc_event_log_visualizer/proto/chart.proto +++ b/rtc_tools/rtc_event_log_visualizer/proto/chart.proto @@ -33,4 +33,8 @@ message Chart { message ChartCollection { repeated Chart charts = 1; + // `calltime_to_utc_ms` is the UTC time (in ms) for the x-axis in the charts. + // In other words, time t ms in the charts corresponds to + // t+calltime_to_utc_ms ms in UTC time. + int64 calltime_to_utc_ms = 2; } diff --git a/rtc_tools/unpack_aecdump/unpack.cc b/rtc_tools/unpack_aecdump/unpack.cc index 0850e75377..49b62d2582 100644 --- a/rtc_tools/unpack_aecdump/unpack.cc +++ b/rtc_tools/unpack_aecdump/unpack.cc @@ -28,7 +28,6 @@ #include "common_audio/wav_file.h" #include "modules/audio_processing/test/protobuf_utils.h" #include "modules/audio_processing/test/test_utils.h" -#include "rtc_base/format_macros.h" #include "rtc_base/ignore_wundef.h" #include "rtc_base/strings/string_builder.h" @@ -480,14 +479,11 @@ int do_main(int argc, char* argv[]) { fprintf(settings_file, " Reverse sample rate: %d\n", reverse_sample_rate); num_input_channels = msg.num_input_channels(); - fprintf(settings_file, " Input channels: %" RTC_PRIuS "\n", - num_input_channels); + fprintf(settings_file, " Input channels: %zu\n", num_input_channels); num_output_channels = msg.num_output_channels(); - fprintf(settings_file, " Output channels: %" RTC_PRIuS "\n", - num_output_channels); + fprintf(settings_file, " Output channels: %zu\n", num_output_channels); num_reverse_channels = msg.num_reverse_channels(); - fprintf(settings_file, " Reverse channels: %" RTC_PRIuS "\n", - num_reverse_channels); + fprintf(settings_file, " Reverse channels: %zu\n", num_reverse_channels); if (msg.has_timestamp_ms()) { const int64_t timestamp = msg.timestamp_ms(); fprintf(settings_file, " Timestamp in millisecond: %" PRId64 "\n", diff --git a/rtc_tools/video_file_reader.cc b/rtc_tools/video_file_reader.cc index c6fa5870e8..6e722b1235 100644 --- a/rtc_tools/video_file_reader.cc +++ b/rtc_tools/video_file_reader.cc @@ -204,9 +204,10 @@ rtc::scoped_refptr