diff --git a/.gn b/.gn index aea8f2c650..c9824916ad 100644 --- a/.gn +++ b/.gn @@ -68,4 +68,11 @@ default_args = { # Prevent jsoncpp to pass -Wno-deprecated-declarations to users jsoncpp_no_deprecated_declarations = false + + # Fixes the abi-revision issue. + # TODO(https://bugs.webrtc.org/14437): Remove this section if general + # Chromium fix resolves the problem. + fuchsia_sdk_readelf_exec = + "//third_party/llvm-build/Release+Asserts/bin/llvm-readelf" + fuchsia_target_api_level = 9 } diff --git a/.vpython3 b/.vpython3 index 39d735dcd1..31a2c59009 100644 --- a/.vpython3 +++ b/.vpython3 @@ -56,6 +56,12 @@ wheel: < > > +# GRPC used by iOS test. +wheel: < + name: "infra/python/wheels/grpcio/${vpython_platform}" + version: "version:1.44.0" +> + wheel: < name: "infra/python/wheels/six-py2_py3" version: "version:1.15.0" @@ -73,8 +79,8 @@ wheel: < version: "version:2.0.0" > wheel: < - name: "infra/python/wheels/protobuf-py2_py3" - version: "version:3.13.0" + name: "infra/python/wheels/protobuf-py3" + version: "version:3.20.0" > wheel: < name: "infra/python/wheels/requests-py2_py3" diff --git a/AUTHORS b/AUTHORS index 68653f4e0d..b3cbb4db35 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,7 +24,10 @@ Anil Kumar Ben Strong Berthold Herrmann Bob Withers +Brett Hebert +Brett Hebert Bridger Maxwell +Bruno Pitrus Cheng Qian Christophe Dumez Chris Tserng @@ -65,6 +68,7 @@ Jose Antonio Olivera Ortega Keiichi Enomoto Kiran Thind Korniltsev Anatoly +Kyutae Lee Lennart Grahl Luke Weber Maksim Khobat @@ -76,11 +80,15 @@ Maksim Sisov Maxim Pavlov Maxim Potapov Michael Iedema +Michał Zarach Michel Promonet Miguel Paris Mike Gilbert +Mike Wei Min Wang +Mike Woodworth Mo Zanaty +Nico Schlumprecht Niek van der Maas Olivier Crête Pali Rohar @@ -101,7 +109,9 @@ Sarah Thompson Satender Saroha Saul Kravitz Sergio Garcia Murillo +Shaofan Qi Shuhai Peng +Seija Silviu Caragea Stefan Gula Stephan Hartmann @@ -132,13 +142,18 @@ Pengfei Han Agora IO <*@agora.io> ARM Holdings <*@arm.com> BroadSoft Inc. <*@broadsoft.com> +Canonical Ltd <*@canonical.com> CoSMo Software Consulting, Pte Ltd <*@cosmosoftware.io> +Discord Inc. <*@discordapp.com> Facebook Inc. <*@fb.com> Google Inc. <*@google.com> Highfive, Inc. <*@highfive.com> +Hopin Ltd. <*@hopin.to> HyperConnect Inc. <*@hpcnt.com> Intel Corporation <*@intel.com> +LG Electronics, Inc. <*@lge.com> Life On Air Inc. <*@lifeonair.com> +Meta Platforms, Inc. <*@meta.com> Microsoft Corporation <*@microsoft.com> MIPS Technologies <*@mips.com> Mozilla Foundation <*@mozilla.com> @@ -148,6 +163,7 @@ NVIDIA Corporation <*@nvidia.com> Opera Software ASA <*@opera.com> Optical Tone Ltd <*@opticaltone.com> Pengutronix e.K. <*@pengutronix.de> +Quebic Inc. <*@quebic.com> Raptor Computing Systems, LLC <*@raptorcs.com> RingCentral, Inc. <*@ringcentral.com> Signal Messenger, LLC <*@signal.org> @@ -165,6 +181,7 @@ Videona Socialmedia <*@videona.com> Videxio AS <*@videxio.com> Vidyo, Inc. <*@vidyo.com> Vonage Holdings Corp. <*@vonage.com> +Wang Qing Wire Swiss GmbH <*@wire.com> &yet LLC <*@andyet.com> # END organizations section. diff --git a/BUILD.gn b/BUILD.gn index 66b3c52ec2..5817d22227 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -67,6 +67,7 @@ if (!build_with_chromium) { "pc:peerconnection_unittests", "pc:rtc_pc_unittests", "pc:slow_peer_connection_unittests", + "pc:svc_tests", "rtc_tools:rtp_generator", "rtc_tools:video_replay", "stats:rtc_stats_unittests", @@ -105,6 +106,12 @@ if (!build_with_chromium) { "tools_webrtc/perf:webrtc_dashboard_upload", ] } + if ((is_linux || is_chromeos) && rtc_use_pipewire) { + deps += [ "modules/desktop_capture:shared_screencast_stream_test" ] + } + if (is_fuchsia) { + deps += [ ":fuchsia_perf_tests" ] + } } if (target_os == "android") { deps += [ "tools_webrtc:binary_version_check" ] @@ -269,6 +276,12 @@ config("common_config") { defines += [ "WEBRTC_ENABLE_PROTOBUF=0" ] } + if (rtc_strict_field_trials) { + defines += [ "WEBRTC_STRICT_FIELD_TRIALS=1" ] + } else { + defines += [ "WEBRTC_STRICT_FIELD_TRIALS=0" ] + } + if (rtc_include_internal_audio_device) { defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ] } @@ -317,9 +330,15 @@ config("common_config") { defines += [ "WEBRTC_EXCLUDE_AUDIO_PROCESSING_MODULE" ] } - # TODO(webrtc:13219): Fix -Wshadow instances and enable. if (is_clang) { - cflags += [ "-Wno-shadow" ] + cflags += [ + # TODO(webrtc:13219): Fix -Wshadow instances and enable. + "-Wno-shadow", + + # See https://reviews.llvm.org/D56731 for details about this + # warning. + "-Wctad-maybe-unsupported", + ] } if (build_with_chromium) { @@ -356,10 +375,14 @@ config("common_config") { } if (is_clang) { - cflags += [ - "-Wc++11-narrowing", - "-Wundef", - ] + cflags += [ "-Wc++11-narrowing" ] + + if (!is_fuchsia) { + # Compiling with the Fuchsia SDK results in Wundef errors + # TODO(bugs.fuchsia.dev/100722): Remove from (!is_fuchsia) branch when + # Fuchsia build errors are fixed. + cflags += [ "-Wundef" ] + } if (!is_nacl) { # Flags NaCl (Clang 3.7) do not recognize. @@ -464,6 +487,7 @@ if (!build_with_chromium) { "api/rtc_event_log:rtc_event_log_factory", "api/task_queue", "api/task_queue:default_task_queue_factory", + "api/test/metrics", "audio", "call", "common_audio", @@ -475,7 +499,6 @@ if (!build_with_chromium) { "p2p:rtc_p2p", "pc:libjingle_peerconnection", "pc:rtc_pc", - "rtc_base", "sdk", "video", ] @@ -551,6 +574,8 @@ if (rtc_include_tests && !build_with_chromium) { "api/audio/test:audio_api_unittests", "api/audio_codecs/test:audio_codecs_api_unittests", "api/numerics:numerics_unittests", + "api/task_queue:pending_task_safety_flag_unittests", + "api/test/metrics:metrics_unittests", "api/transport:stun_unittest", "api/video/test:rtc_api_video_unittests", "api/video_codecs/test:video_codecs_api_unittests", @@ -566,14 +591,12 @@ if (rtc_include_tests && !build_with_chromium) { "rtc_base:rtc_operations_chain_unittests", "rtc_base:rtc_task_queue_unittests", "rtc_base:sigslot_unittest", + "rtc_base:task_queue_stdlib_unittest", "rtc_base:untyped_function_unittest", "rtc_base:weak_ptr_unittests", "rtc_base/experiments:experiments_unittests", "rtc_base/system:file_wrapper_unittests", - "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", @@ -596,10 +619,6 @@ if (rtc_include_tests && !build_with_chromium) { ] shard_timeout = 900 } - - if (is_ios || is_mac) { - deps += [ "sdk:rtc_unittests_objc" ] - } } if (enable_google_benchmarks) { @@ -638,7 +657,6 @@ if (rtc_include_tests && !build_with_chromium) { "test:test_common", "test:test_main", "test:video_test_common", - "video:video_legacy_tests", "video:video_tests", "video/adaptation:video_adaptation_tests", ] @@ -682,7 +700,6 @@ if (rtc_include_tests && !build_with_chromium) { rtc_test("webrtc_perf_tests") { testonly = true deps = [ - "audio:audio_perf_tests", "call:call_perf_tests", "modules/audio_coding:audio_coding_perf_tests", "modules/audio_processing:audio_processing_perf_tests", @@ -707,6 +724,24 @@ if (rtc_include_tests && !build_with_chromium) { } } + if (is_fuchsia) { + rtc_test("fuchsia_perf_tests") { + testonly = true + deps = [ + #TODO(fxbug.dev/115601) - Enable when fixed + "call:call_perf_tests", + "modules/audio_coding:audio_coding_perf_tests", + "modules/audio_processing:audio_processing_perf_tests", + "pc:peerconnection_perf_tests", + "test:test_main", + "video:video_full_stack_tests", + "video:video_pc_full_stack_tests", + ] + + data = webrtc_perf_tests_resources + } + } + rtc_test("webrtc_nonparallel_tests") { testonly = true deps = [ "rtc_base:rtc_base_nonparallel_tests" ] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f6b7d0f6a3..14c4886c02 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,6 @@ + + + # Contributors Code of Conduct Google and the WebRTC team are committed to preserving and fostering a diverse, welcoming and open diff --git a/DEPS b/DEPS index b7b90a7405..2c59464543 100644 --- a/DEPS +++ b/DEPS @@ -10,43 +10,66 @@ vars = { # chromium waterfalls. More info at: crbug.com/570091. 'checkout_configuration': 'default', 'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration == "default"', - 'chromium_revision': 'bca1174cd507f2c0c1eef2d4943cf861be09c772', + 'chromium_revision': 'd76cada1245a655255d71c6166dc5417f2756bd5', + + # Fetch the prebuilt binaries for llvm-cov and llvm-profdata. Needed to + # process the raw profiles produced by instrumented targets (built with + # the gn arg 'use_clang_coverage'). + 'checkout_clang_coverage_tools': False, # Keep the Chromium default of generating location tags. 'generate_location_tags': True, # ResultDB version - 'resultdb_version': 'git_revision:6cc18e2763e180929d70c786b419c1f8e6bcc66c', + 'resultdb_version': 'git_revision:ebc74d10fa0d64057daa6f128e89f3672eeeec95', + + # By default, download the fuchsia sdk from the public sdk directory. + 'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/gn/', + 'fuchsia_version': 'version:11.20230221.2.1', + # By default, download the fuchsia images from the fuchsia GCS bucket. + 'fuchsia_images_bucket': 'fuchsia', + 'checkout_fuchsia': False, + # Since the images are hundreds of MB, default to only downloading the image + # most commonly useful for developers. Bots and developers that need to use + # other images can override this with additional images. + 'checkout_fuchsia_boot_images': "terminal.qemu-x64", + 'checkout_fuchsia_product_bundles': '"{checkout_fuchsia_boot_images}" != ""', + + # reclient CIPD package version + 'reclient_version': 're_client_version:0.96.2.d36a87c-gomaip', + + # ninja CIPD package version + # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja + 'ninja_version': 'version:2@1.11.1.chromium.6', } deps = { - # TODO(kjellander): Move this to be Android-only once the libevent dependency - # in base/third_party/libevent is solved. + # TODO(kjellander): Move this to be Android-only. 'src/base': - 'https://chromium.googlesource.com/chromium/src/base@86e89d2f5c7618cdf8b10946802d2c24160bc9d5', + 'https://chromium.googlesource.com/chromium/src/base@0fc91d5d9071a1619d481fbc6a166fcd9a4ac0a9', 'src/build': - 'https://chromium.googlesource.com/chromium/src/build@08c3a4a86e6cff1335d06ef481e41f129fa3f6ff', + 'https://chromium.googlesource.com/chromium/src/build@07283ef796b29a02df30fbdb498b978f31d00410', 'src/buildtools': - 'https://chromium.googlesource.com/chromium/src/buildtools@7208eddba161d85108097c3c5975264c04e3cad8', + 'https://chromium.googlesource.com/chromium/src/buildtools@d110f6238fee0e4c82ab64606e5e967a3c809c55', # 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@9774f8152da1a93583d340cdd588e78575ca37a7', + 'url': 'https://chromium.googlesource.com/chromium/src/ios@143d6295ffa1eb7446d5e3ee236bc2c67ecf1dc0', 'condition': 'checkout_ios', }, 'src/testing': - 'https://chromium.googlesource.com/chromium/src/testing@947e3a4e8a4892eb0e59b1c9d53929ae000191eb', + 'https://chromium.googlesource.com/chromium/src/testing@b93e19cb405e9c39c414470ce1bca7e17aaae347', 'src/third_party': - 'https://chromium.googlesource.com/chromium/src/third_party@6a087b22ff656342208fbe97202722606c235ea3', + 'https://chromium.googlesource.com/chromium/src/third_party@82835c7ebd240b08997ee36a42eae06b9e386b95', 'src/buildtools/linux64': { 'packages': [ { - 'package': 'gn/gn/linux-amd64', - 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', + 'package': 'gn/gn/linux-${{arch}}', + 'version': 'git_revision:b25a2f8c2d33f02082f0f258350f5e22c0973108', } ], 'dep_type': 'cipd', @@ -56,7 +79,7 @@ deps = { 'packages': [ { 'package': 'gn/gn/mac-${{arch}}', - 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', + 'version': 'git_revision:b25a2f8c2d33f02082f0f258350f5e22c0973108', } ], 'dep_type': 'cipd', @@ -66,27 +89,49 @@ deps = { 'packages': [ { 'package': 'gn/gn/windows-amd64', - 'version': 'git_revision:578a7fe4c3c6b0bc2ae1fd2e37f14857d09895bf', + 'version': 'git_revision:b25a2f8c2d33f02082f0f258350f5e22c0973108', } ], 'dep_type': 'cipd', 'condition': 'checkout_win', }, + 'src/buildtools/reclient': { + 'packages': [ + { + # https://chrome-infra-packages.appspot.com/p/infra/rbe/client/ + 'package': 'infra/rbe/client/${{platform}}', + 'version': Var('reclient_version'), + } + ], + 'dep_type': 'cipd', + # Reclient doesn't have linux-arm64 package. + 'condition': 'not (host_os == "linux" and host_cpu == "arm64")', + }, 'src/buildtools/clang_format/script': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@e435ad79c17b1888b34df88d6a30a094936e3836', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@f97059df7f8b205064625cdb5f97b56668a125ef', 'src/buildtools/third_party/libc++/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@79a2e924d96e2fc1e4b937c42efd08898fa472d7', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git@abad4bb576d98d81c2d5861c3862cc87145f7dad', 'src/buildtools/third_party/libc++abi/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@bb1e46cb72b89a8bb61cc16ad33267b53889aae3', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git@5559ffe8ca32471e894f4cb04b7d18d28862677f', 'src/buildtools/third_party/libunwind/trunk': - 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@3e0d3ec5851c065a401dfe1bb5161da6a1edb192', + 'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git@aecf8d01b908e9dbfe6741da2fe7425879df3537', + + 'src/third_party/ninja': { + 'packages': [ + { + 'package': 'infra/3pp/tools/ninja/${{platform}}', + 'version': Var('ninja_version'), + } + ], + 'dep_type': 'cipd', + }, 'src/third_party/android_system_sdk': { 'packages': [ { 'package': 'chromium/third_party/android_system_sdk/public', - 'version': 'oSfDIvHlCa6W0gS79Q5OOfB9E4eBg3uAvi3BEDN21U0C', + 'version': 'RGY8Vyf8jjszRIJRFxZj7beXSUEHTQM90MtYejUvdMgC', }, ], 'condition': 'checkout_android', @@ -106,7 +151,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/aapt2', - 'version': 'kZqQH92bSO1p0a7_hcrana_9YjtSBU1te7TEtNVBoCUC', + 'version': '36NqCian2RIwuM6SFfizdUgKoXyZhy3q6pFfsws0szYC', }, ], 'condition': 'checkout_android', @@ -117,7 +162,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_build_tools/bundletool', - 'version': 'AqsPZpWJh-ZyGraHKlbH8XgjRnmyDmolX4HhwPEo9XUC', + 'version': 'TpDdbF-PPgwL0iOVsdLM07L-DUp2DV3hgzCMmPd2_GUC', }, ], 'condition': 'checkout_android', @@ -125,11 +170,11 @@ deps = { }, 'src/third_party/boringssl/src': - 'https://boringssl.googlesource.com/boringssl.git@227ff6e6425283b83594a91a1aa81cc78f1a88df', + 'https://boringssl.googlesource.com/boringssl.git@ca1690e221677cea3fb946f324eb89d846ec53f2', 'src/third_party/breakpad/breakpad': - 'https://chromium.googlesource.com/breakpad/breakpad.git@8b68c72a3fff2bb687c7f411e5c1c09e356b8603', + 'https://chromium.googlesource.com/breakpad/breakpad.git@abb105db21e962eda5b7d9b7a0ac8dd701e0b987', 'src/third_party/catapult': - 'https://chromium.googlesource.com/catapult.git@e9b55266586c1188837e156587ff75221ed18120', + 'https://chromium.googlesource.com/catapult.git@a22c2597b5bfa481324838866945c3ed7394ac47', 'src/third_party/ced/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/compact_enc_det.git@ba412eaaacd3186085babcd901679a48863c7dd5', }, @@ -138,31 +183,29 @@ 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@9997ceb9a1c8680b029e85dc9fe7515dec23cf69', + 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@30136f0cb715c04b8e3fb7aec59338f1563a76ae', 'src/third_party/ffmpeg': - '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', - }, + 'https://chromium.googlesource.com/chromium/third_party/ffmpeg.git@ee0c52d52036ecadfd38affec86c04937480bedb', + 'src/third_party/flatbuffers/src': + 'https://chromium.googlesource.com/external/github.com/google/flatbuffers.git@a56f9ec50e908362e20254fcef28e62a2f148d91', 'src/third_party/grpc/src': { - 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@ee2b75e33740d1a88c0e2aeec1b14435e17a889e', + 'url': 'https://chromium.googlesource.com/external/github.com/grpc/grpc.git@a017e9b7f20743c69627b94d7d101e4e6baadb44', }, # Used for embedded builds. CrOS & Linux use the system version. 'src/third_party/fontconfig/src': { - 'url': 'https://chromium.googlesource.com/external/fontconfig.git@452be8125f0e2a18a7dfef469e05d19374d36307', + 'url': 'https://chromium.googlesource.com/external/fontconfig.git@2ef790a0dbbab24235d1b8c0325ab4414de5f0a9', 'condition': 'checkout_linux', }, 'src/third_party/freetype/src': - 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@5d49473f8579d7f5f687d3fe52af977468f8e090', + 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@7f9499044e3baa901de99251a007aa66e750b26c', 'src/third_party/harfbuzz-ng/src': - 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@6454cec085ba51cefcd12b1f8027bc4a647347d5', + 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@2822b589bc837fae6f66233e2cf2eef0f6ce8470', 'src/third_party/google_benchmark/src': { 'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@f730846b0a3c0dc0699978846fb14ffb2fad0bdc', }, # WebRTC-only dependency (not present in Chromium). 'src/third_party/gtest-parallel': - 'https://chromium.googlesource.com/external/github.com/google/gtest-parallel@11cce5c2872be4849c087afc7d19fbed390fa928', + 'https://chromium.googlesource.com/external/github.com/google/gtest-parallel@f4d65b555894b301699c7c3c52906f72ea052e83', 'src/third_party/google-truth': { 'packages': [ { @@ -176,57 +219,81 @@ deps = { 'src/third_party/googletest/src': 'https://chromium.googlesource.com/external/github.com/google/googletest.git@af29db7ec28d6df1c7f0f745186884091e602e07', 'src/third_party/icu': { - 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@585942f33d939a11f4600bd5042649b7ca189008', + 'url': 'https://chromium.googlesource.com/chromium/deps/icu.git@c6b68522318204f795a8f04caebf6c0beb679cc4', }, 'src/third_party/jdk': { 'packages': [ { 'package': 'chromium/third_party/jdk', - 'version': 'JhpgSvTpgVUkoKe56yQmYaR1jXNcY8NqlltA0mKIO4EC', + 'version': '-FR8HTNcMfxy7J2HUaWVa0QmEE4f68iotzvFbqOj2LEC', }, ], 'condition': 'host_os == "linux" and checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/jdk/extras': { + # Deprecated - only use for tools which are broken real JDK. + # Not used by WebRTC. Added for compatibility with Chromium. + 'src/third_party/jdk11': { 'packages': [ { - 'package': 'chromium/third_party/jdk/extras', - 'version': '-7m_pvgICYN60yQI3qmTj_8iKjtnT4NXicT0G_jJPqsC', + 'package': 'chromium/third_party/jdk', + # Do not update this hash - any newer hash will point to JDK17+. + 'version': 'egbcSHbmF1XZQbKxp_PQiGLFWlQK65krTGqQE-Bj4j8C', }, - ], - 'condition': 'host_os == "linux" and checkout_android', + ], + 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/jsoncpp/source': + 'src/third_party/jsoncpp/source': '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', + 'url': 'https://chromium.googlesource.com/external/junit.git@05fe2a64f59127c02135be22f416e91260d6ede6', 'condition': 'checkout_android', }, + 'src/third_party/kotlin_stdlib': { + 'packages': [ + { + 'package': 'chromium/third_party/kotlin_stdlib', + 'version': 'Mg7371mEUwDQH4_z29HdWqYWVlXN6t2dXX0kIutg_SwC', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/kotlinc/current': { + 'packages': [ + { + 'package': 'chromium/third_party/kotlinc', + 'version': 'bCZedwoM-hb1pP1QKzA3P5aR4zjZltqLj4JQpmQsHuUC', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, # Used for building libFuzzers (only supports Linux). '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@22f1a22c99e9dde8cd3c72ead333f425c5a7aa77', + 'https://chromium.googlesource.com/chromium/deps/libjpeg_turbo.git@0b6e6a152242c2fa30ffb29633d1b7d7b229b46b', '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@87f9a81cd770e49394a45deca7a3df41243de00b', + 'https://chromium.googlesource.com/external/github.com/videolan/dav1d.git@ed63a7459376a21e88b871006574dc2055a2ea35', 'src/third_party/libaom/source/libaom': - 'https://aomedia.googlesource.com/aom.git@ef14518388c0a41c1d3b992f75d5886c9da33832', + 'https://aomedia.googlesource.com/aom.git@70b12695e1967d9589dd15b345a039e575e8f429', 'src/third_party/libunwindstack': { - 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@3c86843ae0f8d560ae0d15b92e34ce88cf83057a', + 'url': 'https://chromium.googlesource.com/chromium/src/third_party/libunwindstack.git@4dbfa0e8c844c8e243b297bc185e54a99ff94f9e', 'condition': 'checkout_android', }, 'src/third_party/perfetto': - 'https://android.googlesource.com/platform/external/perfetto.git@b3694fd90110c28532e47831bd1ffc517d0ccc1b', + 'https://android.googlesource.com/platform/external/perfetto.git@55985e77ff4f3e023d321c7f7236e8cfe098e545', 'src/third_party/libvpx/source/libvpx': - 'https://chromium.googlesource.com/webm/libvpx.git@cb1abee1455ac7e552da271ac64c71d117caaa77', + 'https://chromium.googlesource.com/webm/libvpx.git@db69ce6aea278bee88668fd9cc2af2e544516fdb', 'src/third_party/libyuv': - 'https://chromium.googlesource.com/libyuv/libyuv.git@d62ee21e6627888e84466b5a5ed15775582ac67b', + 'https://chromium.googlesource.com/libyuv/libyuv.git@2bdc210be9eb11ded16bf3ef1f6cadb0d4dcb0c2', 'src/third_party/lss': { - 'url': 'https://chromium.googlesource.com/linux-syscall-support.git@92a65a8f5d705d1928874420c8d0d15bde8c89e5', + 'url': 'https://chromium.googlesource.com/linux-syscall-support.git@ce877209e11aa69dcfffbd53ef90ea1d07136521', 'condition': 'checkout_android or checkout_linux', }, 'src/third_party/mockito/src': { @@ -236,26 +303,29 @@ deps = { # Used by boringssl. 'src/third_party/nasm': { - 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@9215e8e1d0fe474ffd3e16c1a07a0f97089e6224' + 'url': 'https://chromium.googlesource.com/chromium/deps/nasm.git@7fc833e889d1afda72c06220e5bed8fb43b2e5ce' }, 'src/third_party/openh264/src': - 'https://chromium.googlesource.com/external/github.com/cisco/openh264@fac04ceb3e966f613ed17e98178e9d690280bba6', + 'https://chromium.googlesource.com/external/github.com/cisco/openh264@db956674bbdfbaab5acdd3fdb4117c2fef5527e9', 'src/third_party/r8': { 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'ovozeRSDDfERnEFpDo_WS6OYOcEF7oT1JzGxCSf-g0kC', + 'version': 'PwglNZFRNPkBBXdnY9NfrZFk2ULWDTRxhV9rl2kvkpUC', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/proguard': { + # This duplication is intentional, so we avoid updating the r8.jar used by + # dexing unless necessary, since each update invalidates all incremental + # dexing and unnecessarily slows down all bots. + 'src/third_party/r8/d8': { 'packages': [ { - 'package': 'chromium/third_party/proguard', - 'version': 'Fd91BJFVlmiO6c46YMTsdy7n2f5Sk2hVVGlzPLvqZPsC', + 'package': 'chromium/third_party/r8', + 'version': 'PwglNZFRNPkBBXdnY9NfrZFk2ULWDTRxhV9rl2kvkpUC', }, ], 'condition': 'checkout_android', @@ -265,17 +335,8 @@ deps = { 'url': 'https://chromium.googlesource.com/external/github.com/kennethreitz/requests.git@refs/tags/v2.23.0', 'condition': 'checkout_android', }, - 'src/third_party/ub-uiautomator/lib': { - 'url': 'https://chromium.googlesource.com/chromium/third_party/ub-uiautomator.git@00270549ce3161ae72ceb24712618ea28b4f9434', - 'condition': 'checkout_android', - }, - # 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@685f07470702cdbe83796519133de9f29f1c046b', + 'https://chromium.googlesource.com/chromium/src/tools@6e72739081a707d893fab3016005273494428319', 'src/third_party/accessibility_test_framework': { 'packages': [ @@ -299,18 +360,7 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/bouncycastle': { - 'packages': [ - { - 'package': 'chromium/third_party/bouncycastle', - 'version': 'c078e87552ba26e776566fdaf0f22cd8712743d0', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/byte_buddy': { + 'src/third_party/byte_buddy': { 'packages': [ { 'package': 'chromium/third_party/byte_buddy', @@ -363,22 +413,33 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': '_6LSpTMrwppYMyP7cQHuhfab0hq8aJFpArKyFPpd9wgC', + 'version': 'waVlDuvM1_o0siiUN39LBaTpj0pfqoBRglXQgdicJicC', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, + 'src/third_party/android_build_tools/manifest_merger': { + 'packages': [ + { + 'package': 'chromium/third_party/android_build_tools/manifest_merger', + 'version': 'EbRaK62t9grqlZqL-JTd_zwM4t1u9fm1x4c2rLE0cqQC', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + 'src/third_party/android_sdk/public': { 'packages': [ { - 'package': 'chromium/third_party/android_sdk/public/build-tools/31.0.0', - 'version': 'tRoD45SCi7UleQqSV7MrMQO1_e5P8ysphkCcj6z_cCQC', + 'package': 'chromium/third_party/android_sdk/public/build-tools/33.0.0', + 'version': '-VRKr36Uw8L_iFqqo9nevIBgNMggND5iWxjidyjnCgsC', }, { 'package': 'chromium/third_party/android_sdk/public/emulator', - 'version': 'gMHhUuoQRKfxr-MBn3fNNXZtkAVXtOwMwT7kfx8jkIgC', + 'version': '9lGp8nTUCRRWGMnI_96HcKfzjnxEJKUcfvfwmA3wXNkC', }, { 'package': 'chromium/third_party/android_sdk/public/patcher', @@ -386,11 +447,15 @@ deps = { }, { 'package': 'chromium/third_party/android_sdk/public/platform-tools', - 'version': 'g7n_-r6yJd_SGRklujGB1wEt8iyr77FZTUJVS9w6O34C', + 'version': 'RSI3iwryh7URLGRgJHsCvUxj092woTPnKt4pwFcJ6L8C', }, { - 'package': 'chromium/third_party/android_sdk/public/platforms/android-31', - 'version': 'lL3IGexKjYlwjO_1Ga-xwxgwbE_w-lmi2Zi1uOlWUIAC', + 'package': 'chromium/third_party/android_sdk/public/platforms/android-33', + 'version': 'eo5KvW6UVor92LwZai8Zulc624BQZoCu-yn7wa1z_YcC', + }, + { + 'package': 'chromium/third_party/android_sdk/public/platforms/android-tiramisuprivacysandbox', + 'version': 'YWMYkzyxGBgVsty0GhXL1oxbY0pGXQIgFc0Rh7ZMRPYC', }, { 'package': 'chromium/third_party/android_sdk/public/sources/android-31', @@ -398,7 +463,7 @@ deps = { }, { 'package': 'chromium/third_party/android_sdk/public/cmdline-tools', - 'version': 'PGPmqJtSIQ84If155ba7iTU846h5WJ-bL5d_OoUWEWYC', + 'version': '3Yn5Sn7BMObm8gsoZCF0loJMKg9_PpgU07G9DObCLdQC', }, ], 'condition': 'checkout_android', @@ -431,7 +496,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/robolectric', - 'version': 'WZ96VJuhBM63xzHb-_E72Tf46M9yIbfia6basI1YG4EC', + 'version': 'hzetqh1qFI32FOgQroZvGcGdomrgVBJ6WKRnl1KFw6EC', }, ], 'condition': 'checkout_android', @@ -453,7 +518,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/turbine', - 'version': 'FJ-IOPRGQsHUZwVeYmVw_idRk5mUUP6_Uj2i6mKQlEMC', + 'version': 'YQC-btuan_DTe9V9dv_e1LxgYSWeOoDfrd-VSqzIvHkC', }, ], 'condition': 'checkout_android', @@ -464,15 +529,30 @@ deps = { 'packages': [ { 'package': 'infra/tools/luci/isolate/${{platform}}', - 'version': 'git_revision:2aa3d7e5e8662c5193059a490f07b7d91331933e', + 'version': 'git_revision:c543f47ae455dbfe7e8fed5baa61a14d9068e98c', }, { 'package': 'infra/tools/luci/swarming/${{platform}}', - 'version': 'git_revision:2aa3d7e5e8662c5193059a490f07b7d91331933e', + 'version': 'git_revision:c543f47ae455dbfe7e8fed5baa61a14d9068e98c', }, ], 'dep_type': 'cipd', }, + 'src/third_party/pipewire/linux-amd64': { + 'packages': [ + { + 'package': 'chromium/third_party/pipewire/linux-amd64', + 'version': 'BaVKmAmwpjdS6O0pnjSaMNSKhO1nmk5mRnyPVAJ2-HEC', + }, + { + 'package': 'chromium/third_party/pipewire-media-session/linux-amd64', + 'version': 'Y6wUeITvAA0QD1vt8_a7eQdzbp0gkI1B02qfZUMJdowC', + }, + ], + + 'condition': 'checkout_linux', + 'dep_type': 'cipd', + }, # Everything coming after this is automatically updated by the auto-roller. # === ANDROID_DEPS Generated Code Start === @@ -906,28 +986,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs', - 'version': 'version:2@1.1.5.cr1', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - - 'src/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration', - 'version': 'version:2@1.1.5.cr1', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - 'src/third_party/android_deps/libs/com_android_tools_layoutlib_layoutlib_api': { 'packages': [ { @@ -972,11 +1030,22 @@ deps = { 'dep_type': 'cipd', }, + 'src/third_party/android_deps/libs/com_google_android_annotations': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/com_google_android_annotations', + 'version': 'version:2@4.1.1.4.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', + 'version': 'version:2@4.0.0.cr1', }, ], 'condition': 'checkout_android', @@ -1042,7 +1111,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_basement', - 'version': 'version:2@18.0.1.cr1', + 'version': 'version:2@18.1.0.cr1', }, ], 'condition': 'checkout_android', @@ -1196,7 +1265,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_gms_play_services_tasks', - 'version': 'version:2@18.0.1.cr1', + 'version': 'version:2@18.0.2.cr1', }, ], 'condition': 'checkout_android', @@ -1229,18 +1298,29 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_android_material_material', - 'version': 'version:2@1.6.0-alpha01.cr1', + 'version': 'version:2@1.7.0-alpha02.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/com_google_android_play_core_common': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core_common', + 'version': 'version:2@2.0.2.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/com_google_android_play_core': { + 'src/third_party/android_deps/libs/com_google_android_play_feature_delivery': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/com_google_android_play_core', - 'version': 'version:2@1.10.0.cr1', + 'package': 'chromium/third_party/android_deps/libs/com_google_android_play_feature_delivery', + 'version': 'version:2@2.0.1.cr1', }, ], 'condition': 'checkout_android', @@ -1306,7 +1386,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_code_gson_gson', - 'version': 'version:2@2.8.0.cr1', + 'version': 'version:2@2.9.0.cr1', }, ], 'condition': 'checkout_android', @@ -1372,7 +1452,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations', - 'version': 'version:2@2.11.0.cr1', + 'version': 'version:2@2.14.0.cr1', }, ], 'condition': 'checkout_android', @@ -1555,17 +1635,6 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/com_google_flatbuffers_flatbuffers_java': { - 'packages': [ - { - 'package': 'chromium/third_party/android_deps/libs/com_google_flatbuffers_flatbuffers_java', - 'version': 'version:2@2.0.3.cr1', - }, - ], - 'condition': 'checkout_android', - 'dep_type': 'cipd', - }, - 'src/third_party/android_deps/libs/com_google_googlejavaformat_google_java_format': { 'packages': [ { @@ -1603,7 +1672,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava_android', - 'version': 'version:2@31.0-android.cr1', + 'version': 'version:2@31.1-android.cr1', }, ], 'condition': 'checkout_android', @@ -1647,18 +1716,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_protobuf_protobuf_javalite', - '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', + 'version': 'version:2@3.21.1.cr1', }, ], 'condition': 'checkout_android', @@ -1709,6 +1767,83 @@ deps = { 'dep_type': 'cipd', }, + 'src/third_party/android_deps/libs/io_grpc_grpc_api': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_api', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_grpc_grpc_binder': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_binder', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_grpc_grpc_context': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_context', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_grpc_grpc_core': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_core', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_grpc_grpc_protobuf_lite': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_protobuf_lite', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_grpc_grpc_stub': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_grpc_grpc_stub', + 'version': 'version:2@1.49.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/io_perfmark_perfmark_api': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/io_perfmark_perfmark_api', + 'version': 'version:2@0.25.0.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + 'src/third_party/android_deps/libs/javax_annotation_javax_annotation_api': { 'packages': [ { @@ -1742,6 +1877,28 @@ deps = { 'dep_type': 'cipd', }, + 'src/third_party/android_deps/libs/net_bytebuddy_byte_buddy': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/net_bytebuddy_byte_buddy', + 'version': 'version:2@1.12.22.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/net_bytebuddy_byte_buddy_agent', + 'version': 'version:2@1.12.22.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + 'src/third_party/android_deps/libs/net_ltgt_gradle_incap_incap': { 'packages': [ { @@ -1764,110 +1921,110 @@ deps = { 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup': { + 'src/third_party/android_deps/libs/org_bouncycastle_bcprov_jdk15on': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup', - 'version': 'version:2@1.2.1.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_bouncycastle_bcprov_jdk15on', + 'version': 'version:2@1.68.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_checkerframework_checker_compat_qual': { + 'src/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual', - 'version': 'version:2@2.5.5.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_ccil_cowan_tagsoup_tagsoup', + 'version': 'version:2@1.2.1.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_checkerframework_checker_qual': { + 'src/third_party/android_deps/libs/org_checkerframework_checker_compat_qual': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual', - 'version': 'version:2@3.12.0.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual', + 'version': 'version:2@2.5.5.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone': { + 'src/third_party/android_deps/libs/org_checkerframework_checker_qual': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone', - 'version': 'version:2@3.15.0.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual', + 'version': 'version:2@3.25.0.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations': { + 'src/third_party/android_deps/libs/org_checkerframework_checker_util': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations', - 'version': 'version:2@1.17.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_util', + 'version': 'version:2@3.25.0.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_checkerframework_dataflow_errorprone': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit', - 'version': 'version:2@4.4.1.201607150455-r.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_dataflow_errorprone', + 'version': 'version:2@3.15.0.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_hamcrest_hamcrest': { + 'src/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_hamcrest_hamcrest', - 'version': 'version:2@2.2.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_codehaus_mojo_animal_sniffer_annotations', + 'version': 'version:2@1.21.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_jetbrains_annotations': { + 'src/third_party/android_deps/libs/org_conscrypt_conscrypt_openjdk_uber': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_annotations', - 'version': 'version:2@13.0.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_conscrypt_conscrypt_openjdk_uber', + 'version': 'version:2@2.5.2.cr1', }, ], 'condition': 'checkout_android', 'dep_type': 'cipd', }, - 'src/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib': { + 'src/third_party/android_deps/libs/org_eclipse_jgit_org_eclipse_jgit': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib', - 'version': 'version:2@1.6.21.cr1', + '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_jetbrains_kotlin_kotlin_stdlib_common': { + 'src/third_party/android_deps/libs/org_hamcrest_hamcrest': { 'packages': [ { - 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_common', - 'version': 'version:2@1.6.21.cr1', + 'package': 'chromium/third_party/android_deps/libs/org_hamcrest_hamcrest', + 'version': 'version:2@2.2.cr1', }, ], 'condition': 'checkout_android', @@ -1878,7 +2035,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk7', - 'version': 'version:2@1.6.20.cr1', + 'version': 'version:2@1.8.0.cr1', }, ], 'condition': 'checkout_android', @@ -1889,7 +2046,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jetbrains_kotlin_kotlin_stdlib_jdk8', - 'version': 'version:2@1.6.20.cr1', + 'version': 'version:2@1.8.0.cr1', }, ], 'condition': 'checkout_android', @@ -1933,7 +2090,29 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_jsoup_jsoup', - 'version': 'version:2@1.14.3.cr1', + 'version': 'version:2@1.15.1.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_mockito_mockito_core': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_mockito_mockito_core', + 'version': 'version:2@5.1.1.cr1', + }, + ], + 'condition': 'checkout_android', + 'dep_type': 'cipd', + }, + + 'src/third_party/android_deps/libs/org_objenesis_objenesis': { + 'packages': [ + { + 'package': 'chromium/third_party/android_deps/libs/org_objenesis_objenesis', + 'version': 'version:2@3.3.cr1', }, ], 'condition': 'checkout_android', @@ -2010,7 +2189,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_annotations', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2021,7 +2200,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_junit', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2032,7 +2211,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_nativeruntime', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2043,7 +2222,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_pluginapi', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2054,7 +2233,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_plugins_maven_dependency_resolver', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2065,7 +2244,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_resources', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2076,7 +2255,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_robolectric', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2087,7 +2266,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_sandbox', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2098,7 +2277,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadowapi', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2109,18 +2288,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_framework', - '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', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2131,7 +2299,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_shadows_playservices', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2142,7 +2310,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2153,7 +2321,7 @@ deps = { 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_robolectric_utils_reflector', - 'version': 'version:2@4.7.3.cr1', + 'version': 'version:2@4.8.1.cr1', }, ], 'condition': 'checkout_android', @@ -2255,12 +2423,42 @@ hooks = [ 'condition': 'checkout_mac', 'action': ['python3', 'src/build/mac_toolchain.py'], }, + + { + 'name': 'Download Fuchsia SDK from GCS', + 'pattern': '.', + 'condition': 'checkout_fuchsia', + 'action': [ + 'python3', + 'src/build/fuchsia/update_sdk.py', + '--cipd-prefix={fuchsia_sdk_cipd_prefix}', + '--version={fuchsia_version}', + ], + }, + { + 'name': 'Download Fuchsia system images', + 'pattern': '.', + 'condition': 'checkout_fuchsia and checkout_fuchsia_product_bundles', + 'action': [ + 'python3', + 'src/build/fuchsia/update_product_bundles.py', + '{checkout_fuchsia_boot_images}', + ], + }, { # Note: On Win, this should run after win_toolchain, as it may use it. 'name': 'clang', 'pattern': '.', 'action': ['python3', 'src/tools/clang/scripts/update.py'], }, + { + # This is supposed to support the same set of platforms as 'clang' above. + 'name': 'clang_coverage', + 'pattern': '.', + 'condition': 'checkout_clang_coverage_tools', + 'action': ['python3', 'src/tools/clang/scripts/update.py', + '--package=coverage_tools'], + }, { # Update LASTCHANGE. 'name': 'lastchange', @@ -2398,27 +2596,27 @@ hooks = [ ], }, { - 'name': 'msan_chained_origins', + 'name': 'msan_chained_origins_focal', 'pattern': '.', 'condition': 'checkout_instrumented_libraries', 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', - "--no_resume", - "--no_auth", - "--bucket", "chromium-instrumented-libraries", - "-s", "src/third_party/instrumented_libraries/binaries/msan-chained-origins.tgz.sha1", + '--no_resume', + '--no_auth', + '--bucket', 'chromium-instrumented-libraries', + '-s', 'src/third_party/instrumented_libraries/binaries/msan-chained-origins-focal.tgz.sha1', ], }, { - 'name': 'msan_no_origins', + 'name': 'msan_no_origins_focal', 'pattern': '.', 'condition': 'checkout_instrumented_libraries', 'action': [ 'python3', 'src/third_party/depot_tools/download_from_google_storage.py', - "--no_resume", - "--no_auth", - "--bucket", "chromium-instrumented-libraries", - "-s", "src/third_party/instrumented_libraries/binaries/msan-no-origins.tgz.sha1", + '--no_resume', + '--no_auth', + '--bucket', 'chromium-instrumented-libraries', + '-s', 'src/third_party/instrumented_libraries/binaries/msan-no-origins-focal.tgz.sha1', ], }, { @@ -2484,11 +2682,13 @@ include_rules = [ "+absl/base/macros.h", "+absl/cleanup/cleanup.h", "+absl/container/inlined_vector.h", + "+absl/functional/any_invocable.h", "+absl/functional/bind_front.h", "+absl/memory/memory.h", "+absl/meta/type_traits.h", "+absl/numeric/bits.h", "+absl/strings/ascii.h", + "+absl/strings/escaping.h", "+absl/strings/match.h", "+absl/strings/str_replace.h", "+absl/strings/string_view.h", diff --git a/OWNERS b/OWNERS index 6ae4b59a95..bfcca980eb 100644 --- a/OWNERS +++ b/OWNERS @@ -3,20 +3,4 @@ hta@webrtc.org mflodman@webrtc.org stefan@webrtc.org tommi@webrtc.org -per-file .gitignore=* -per-file .gn=mbonadei@webrtc.org -per-file BUILD.gn=mbonadei@webrtc.org -per-file .../BUILD.gn=mbonadei@webrtc.org -per-file *.gni=mbonadei@webrtc.org -per-file .../*.gni=mbonadei@webrtc.org -per-file .vpython=mbonadei@webrtc.org -per-file .vpython3=mbonadei@webrtc.org -per-file AUTHORS=* -per-file DEPS=* -per-file pylintrc=mbonadei@webrtc.org -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 +include OWNERS_INFRA #{Owners for infra and repo related files} diff --git a/OWNERS_INFRA b/OWNERS_INFRA new file mode 100644 index 0000000000..7172570152 --- /dev/null +++ b/OWNERS_INFRA @@ -0,0 +1,17 @@ +#Owners for infra and repo related files +per-file .gitignore=* +per-file .gn=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file BUILD.gn=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file .../BUILD.gn=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file *.gni=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file .../*.gni=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file .vpython=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file .vpython3=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +per-file AUTHORS=* +per-file DEPS=* +per-file pylintrc=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org +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=mbonadei@webrtc.org,jansson@webrtc.org,jleconte@webrtc.org diff --git a/PRESUBMIT.py b/PRESUBMIT.py index cf7d261138..796103667a 100755 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -953,7 +953,7 @@ def CommonChecks(input_api, output_api): # Skip long-lines check for DEPS and GN files. build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS') # Also we will skip most checks for third_party directory. - third_party_filter_list = (r'^third_party[\\\/].+', ) + third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', ) eighty_char_sources = lambda x: input_api.FilterSourceFile( x, files_to_skip=build_file_filter_list + objc_filter_list + diff --git a/WATCHLISTS b/WATCHLISTS index 11464b1f69..3c35dd4be7 100644 --- a/WATCHLISTS +++ b/WATCHLISTS @@ -73,9 +73,6 @@ 'video_coding': { 'filepath': 'modules/video_coding/.*', }, - 'video_processing': { - 'filepath': 'modules/video_processing/.*', - }, 'bitrate_controller': { 'filepath': 'modules/bitrate_controller/.*' }, @@ -110,9 +107,7 @@ 'yujie.mao@webrtc.org'], 'build_files': ['mbonadei@webrtc.org'], 'common_audio': ['alessiob@webrtc.org', - 'aluebs@webrtc.org', 'audio-team@agora.io', - 'minyue@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], 'audio': ['peah@webrtc.org'], @@ -138,33 +133,25 @@ 'audio_coding': ['alessiob@webrtc.org', 'audio-team@agora.io', 'henrik.lundin@webrtc.org', - 'minyue@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], 'neteq': ['alessiob@webrtc.org', 'audio-team@agora.io', 'henrik.lundin@webrtc.org', - 'minyue@webrtc.org', 'saza@webrtc.org'], 'audio_mixer': ['aleloi@webrtc.org', 'henrik.lundin@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], 'audio_processing': ['alessiob@webrtc.org', - 'aluebs@webrtc.org', 'audio-team@agora.io', - 'fhernqvist@webrtc.org', 'henrik.lundin@webrtc.org', - 'minyue@webrtc.org', 'peah@webrtc.org', 'saza@webrtc.org'], 'video_coding': ['mflodman@webrtc.org', 'stefan@webrtc.org', 'video-team@agora.io', 'zhengzhonghou@agora.io'], - 'video_processing': ['stefan@webrtc.org', - 'video-team@agora.io', - 'zhengzhonghou@agora.io'], 'bitrate_controller': ['mflodman@webrtc.org', 'stefan@webrtc.org', 'zhuangzesen@agora.io'], diff --git a/api/BUILD.gn b/api/BUILD.gn index 569148065e..d1b2c23491 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn @@ -54,7 +54,6 @@ if (!build_with_chromium) { "../modules/audio_processing:api", "../pc:peer_connection_factory", "../pc:webrtc_sdp", - "../rtc_base", "../rtc_base:threading", "../stats:rtc_stats", "audio:audio_mixer_api", @@ -89,11 +88,12 @@ rtc_library("rtp_packet_info") { ] deps = [ ":array_view", + ":make_ref_counted", ":refcountedbase", ":rtp_headers", ":scoped_refptr", - "../rtc_base:refcount", "../rtc_base/system:rtc_export", + "units:time_delta", "units:timestamp", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] @@ -115,6 +115,7 @@ rtc_library("media_stream_interface") { ] deps = [ ":audio_options_api", + ":make_ref_counted", ":rtp_parameters", ":scoped_refptr", ":sequence_checker", @@ -127,55 +128,154 @@ rtc_library("media_stream_interface") { "video:recordable_encoded_frame", "video:video_frame", ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("candidate") { + visibility = [ "*" ] + + sources = [ + "candidate.cc", + "candidate.h", + ] + deps = [ + "../rtc_base:checks", + "../rtc_base:ip_address", + "../rtc_base:logging", + "../rtc_base:network_constants", + "../rtc_base:socket_address", + "../rtc_base:ssl", + "../rtc_base:stringutils", + "../rtc_base/system:rtc_export", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_source_set("turn_customizer") { + visibility = [ "*" ] + sources = [ "turn_customizer.h" ] + deps = [ "transport:stun_types" ] +} + +rtc_source_set("ice_transport_interface") { + visibility = [ "*" ] + + sources = [ "ice_transport_interface.h" ] + deps = [ + ":async_dns_resolver", + ":packet_socket_factory", + ":rtc_error", + ":scoped_refptr", + "../rtc_base:refcount", + "rtc_event_log:rtc_event_log", + ] +} + +rtc_library("dtls_transport_interface") { + visibility = [ "*" ] + + sources = [ + "dtls_transport_interface.cc", + "dtls_transport_interface.h", + ] + deps = [ + ":ice_transport_interface", + ":rtc_error", + ":scoped_refptr", + "../rtc_base:refcount", + "../rtc_base:ssl", + "../rtc_base/system:rtc_export", + ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } +rtc_library("dtmf_sender_interface") { + visibility = [ "*" ] + + sources = [ "dtmf_sender_interface.h" ] + deps = [ + ":media_stream_interface", + "../rtc_base:refcount", + ] +} + +rtc_library("rtp_sender_interface") { + visibility = [ "*" ] + + sources = [ + "rtp_sender_interface.cc", + "rtp_sender_interface.h", + ] + deps = [ + ":dtls_transport_interface", + ":dtmf_sender_interface", + ":frame_transformer_interface", + ":media_stream_interface", + ":rtc_error", + ":rtp_parameters", + ":scoped_refptr", + "../rtc_base:checks", + "../rtc_base:refcount", + "../rtc_base/system:rtc_export", + "crypto:frame_encryptor_interface", + "video_codecs:video_codecs_api", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/functional:any_invocable" ] +} + rtc_library("libjingle_peerconnection_api") { visibility = [ "*" ] cflags = [] sources = [ - "candidate.cc", - "candidate.h", "crypto_params.h", "data_channel_interface.cc", "data_channel_interface.h", - "dtls_transport_interface.cc", - "dtls_transport_interface.h", - "dtmf_sender_interface.h", - "ice_transport_interface.h", "jsep.cc", "jsep.h", "jsep_ice_candidate.cc", "jsep_ice_candidate.h", "jsep_session_description.h", + "legacy_stats_types.cc", + "legacy_stats_types.h", "peer_connection_interface.cc", "peer_connection_interface.h", "rtp_receiver_interface.cc", "rtp_receiver_interface.h", - "rtp_sender_interface.cc", - "rtp_sender_interface.h", "rtp_transceiver_interface.cc", "rtp_transceiver_interface.h", "sctp_transport_interface.cc", "sctp_transport_interface.h", "set_local_description_observer_interface.h", "set_remote_description_observer_interface.h", - "stats_types.cc", - "stats_types.h", - "turn_customizer.h", "uma_metrics.h", "video_track_source_proxy_factory.h", + + # Remove when downstream has been updated + "dtmf_sender_interface.h", + "rtp_sender_interface.h", + ] + public_deps = [ # no-presubmit-check TODO(webrtc:8603) + # Remove when downstream has been updated + ":dtmf_sender_interface", + ":rtp_sender_interface", ] deps = [ ":array_view", ":async_dns_resolver", ":audio_options_api", ":callfactory_api", + ":candidate", + ":dtls_transport_interface", ":fec_controller_api", ":field_trials", ":field_trials_view", ":frame_transformer_interface", + ":ice_transport_interface", ":libjingle_logging_api", + ":make_ref_counted", ":media_stream_interface", ":network_state_predictor_api", ":packet_socket_factory", @@ -184,14 +284,20 @@ rtc_library("libjingle_peerconnection_api") { ":rtc_stats_api", ":rtp_packet_info", ":rtp_parameters", + ":rtp_sender_interface", ":rtp_transceiver_direction", ":scoped_refptr", ":sequence_checker", + ":turn_customizer", "../call:rtp_interfaces", + "../p2p:rtc_p2p", "../rtc_base:copy_on_write_buffer", "../rtc_base:logging", + "../rtc_base:network", "../rtc_base:network_constants", "../rtc_base:refcount", + "../rtc_base:rtc_certificate_generator", + "../rtc_base:ssl", "../rtc_base:stringutils", "adaptation:resource_adaptation_api", "audio:audio_mixer_api", @@ -214,6 +320,7 @@ rtc_library("libjingle_peerconnection_api") { "video:video_bitrate_allocator_factory", "video:video_frame", "video:video_rtp_headers", + "video_codecs:video_codecs_api", # Basically, don't add stuff here. You might break sensitive downstream # targets like pnacl. API should not depend on anything outside of this @@ -221,7 +328,6 @@ rtc_library("libjingle_peerconnection_api") { "../media:rtc_media_base", "../media:rtc_media_config", "../modules/audio_processing:audio_processing_statistics", - "../rtc_base", "../rtc_base:checks", "../rtc_base:ip_address", "../rtc_base:socket_address", @@ -230,6 +336,8 @@ rtc_library("libjingle_peerconnection_api") { ] absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", @@ -240,6 +348,7 @@ rtc_source_set("frame_transformer_interface") { visibility = [ "*" ] sources = [ "frame_transformer_interface.h" ] deps = [ + ":make_ref_counted", ":scoped_refptr", "../rtc_base:refcount", "video:encoded_frame", @@ -271,8 +380,9 @@ rtc_source_set("packet_socket_factory") { deps = [ ":async_dns_resolver", ":wrapping_async_dns_resolver", + "../rtc_base:async_packet_socket", "../rtc_base:async_resolver_interface", - "../rtc_base:rtc_base", + "../rtc_base:proxy_info", "../rtc_base:socket_address", "../rtc_base/system:rtc_export", ] @@ -282,6 +392,7 @@ rtc_source_set("async_dns_resolver") { visibility = [ "*" ] sources = [ "async_dns_resolver.h" ] deps = [ + "../rtc_base:checks", "../rtc_base:socket_address", "../rtc_base/system:rtc_export", ] @@ -314,6 +425,12 @@ rtc_source_set("scoped_refptr") { sources = [ "scoped_refptr.h" ] } +rtc_source_set("make_ref_counted") { + visibility = [ "*" ] + sources = [ "make_ref_counted.h" ] + deps = [ "../rtc_base:refcount" ] +} + rtc_source_set("video_quality_test_fixture_api") { visibility = [ "*" ] testonly = true @@ -328,6 +445,7 @@ rtc_source_set("video_quality_test_fixture_api") { "../call:rtp_interfaces", "../test:test_common", "../test:video_test_common", + "../video/config:encoder_config", "transport:bitrate_settings", "transport:network_control", "video_codecs:video_codecs_api", @@ -384,8 +502,11 @@ rtc_library("rtp_parameters") { "../rtc_base:checks", "../rtc_base:stringutils", "../rtc_base/system:rtc_export", + "video:resolution", + "video_codecs:scalability_mode", ] absl_deps = [ + "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] @@ -423,7 +544,7 @@ rtc_source_set("peer_network_dependencies") { sources = [ "test/peer_network_dependencies.h" ] deps = [ ":packet_socket_factory", - "../rtc_base", + "../rtc_base:network", "../rtc_base:threading", ] } @@ -431,10 +552,7 @@ rtc_source_set("peer_network_dependencies") { rtc_source_set("peer_connection_quality_test_fixture_api") { visibility = [ "*" ] testonly = true - sources = [ - "test/peerconnection_quality_test_fixture.cc", - "test/peerconnection_quality_test_fixture.h", - ] + sources = [ "test/peerconnection_quality_test_fixture.h" ] deps = [ ":array_view", @@ -453,20 +571,30 @@ rtc_source_set("peer_connection_quality_test_fixture_api") { ":stats_observer_interface", ":track_id_stream_info_map", ":video_quality_analyzer_api", + "../media:media_constants", "../media:rtc_media_base", "../modules/audio_processing:api", "../rtc_base:checks", - "../rtc_base:rtc_base", + "../rtc_base:network", + "../rtc_base:rtc_certificate_generator", + "../rtc_base:ssl", + "../rtc_base:stringutils", "../rtc_base:threading", + "../test:fileutils", "audio:audio_mixer_api", "rtc_event_log", "task_queue", + "test/pclf:media_configuration", + "test/pclf:media_quality_test_params", + "test/pclf:peer_configurer", + "test/video:video_frame_writer", "transport:network_control", "units:time_delta", "video:video_frame", "video_codecs:video_codecs_api", ] absl_deps = [ + "//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", @@ -548,6 +676,7 @@ if (rtc_include_tests) { ":time_controller", ":video_quality_analyzer_api", "../test/pc/e2e:peerconnection_quality_test", + "test/metrics:global_metrics_logger_and_exporter", ] } } @@ -579,9 +708,9 @@ rtc_library("create_peer_connection_quality_test_frame_generator") { deps = [ ":create_frame_generator", ":frame_generator_api", - ":peer_connection_quality_test_fixture_api", "../rtc_base:checks", "../test:fileutils", + "test/pclf:media_configuration", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } @@ -589,6 +718,7 @@ rtc_library("create_peer_connection_quality_test_frame_generator") { rtc_source_set("libjingle_logging_api") { visibility = [ "*" ] sources = [ "rtc_event_log_output.h" ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings:strings" ] } rtc_library("rtc_event_log_output_file") { @@ -618,12 +748,16 @@ rtc_source_set("rtc_stats_api") { ] deps = [ + ":make_ref_counted", ":scoped_refptr", "../api:refcountedbase", "../rtc_base:checks", "../rtc_base:refcount", "../rtc_base/system:rtc_export", + "units:timestamp", ] + + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] } rtc_library("audio_options_api") { @@ -667,7 +801,6 @@ rtc_source_set("simulated_network_api") { visibility = [ "*" ] sources = [ "test/simulated_network.h" ] deps = [ - "../rtc_base", "../rtc_base:macromagic", "../rtc_base:random", ] @@ -688,7 +821,8 @@ rtc_source_set("network_emulation_manager_api") { ":simulated_network_api", ":time_controller", "../call:simulated_network", - "../rtc_base", + "../rtc_base:checks", + "../rtc_base:network", "../rtc_base:network_constants", "../rtc_base:threading", "test/network_emulation", @@ -706,8 +840,6 @@ rtc_source_set("time_controller") { ] deps = [ - "../modules/utility", - "../rtc_base", "../rtc_base:threading", "../rtc_base/synchronization:yield_policy", "../system_wrappers", @@ -760,11 +892,12 @@ rtc_library("ice_transport_factory") { "ice_transport_factory.h", ] deps = [ + ":ice_transport_interface", ":libjingle_peerconnection_api", + ":make_ref_counted", ":packet_socket_factory", ":scoped_refptr", "../p2p:rtc_p2p", - "../rtc_base", "../rtc_base:threading", "../rtc_base/system:rtc_export", "rtc_event_log:rtc_event_log", @@ -854,22 +987,64 @@ if (rtc_include_tests) { ] } - rtc_library("videocodec_test_fixture_api") { + rtc_library("videocodec_test_stats_api") { visibility = [ "*" ] testonly = true sources = [ - "test/videocodec_test_fixture.h", "test/videocodec_test_stats.cc", "test/videocodec_test_stats.h", ] deps = [ - "../modules/video_coding:video_codec_interface", + "../api/units:data_rate", + "../api/units:frequency", "../rtc_base:stringutils", "video:video_frame_type", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + rtc_library("video_codec_stats_api") { + visibility = [ "*" ] + testonly = true + sources = [ "test/video_codec_stats.h" ] + deps = [ + "../api/numerics:numerics", + "../api/units:data_rate", + "../api/units:frequency", + "test/metrics:metric", + "test/metrics:metrics_logger", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + rtc_library("videocodec_test_fixture_api") { + visibility = [ "*" ] + testonly = true + sources = [ "test/videocodec_test_fixture.h" ] + deps = [ + ":videocodec_test_stats_api", + "../modules/video_coding:video_codec_interface", "video_codecs:video_codecs_api", ] } + rtc_library("video_codec_tester_api") { + visibility = [ "*" ] + testonly = true + sources = [ "test/video_codec_tester.h" ] + deps = [ + ":video_codec_stats_api", + "../modules/video_coding/svc:scalability_mode_util", + "video:encoded_image", + "video:resolution", + "video:video_frame", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/functional:any_invocable", + "//third_party/abseil-cpp/absl/types:optional", + ] + } + rtc_library("create_videocodec_test_fixture_api") { visibility = [ "*" ] testonly = true @@ -885,6 +1060,19 @@ if (rtc_include_tests) { ] } + rtc_library("create_video_codec_tester_api") { + visibility = [ "*" ] + testonly = true + sources = [ + "test/create_video_codec_tester.cc", + "test/create_video_codec_tester.h", + ] + deps = [ + ":video_codec_tester_api", + "../modules/video_coding:videocodec_test_impl", + ] + } + rtc_source_set("mock_audio_mixer") { visibility = [ "*" ] testonly = true @@ -925,6 +1113,7 @@ if (rtc_include_tests) { sources = [ "test/mock_dtmf_sender.h" ] deps = [ + ":dtmf_sender_interface", ":libjingle_peerconnection_api", "../test:test_support", ] @@ -963,6 +1152,17 @@ if (rtc_include_tests) { ] } + rtc_library("mock_encoder_selector") { + visibility = [ "*" ] + testonly = true + sources = [ "test/mock_encoder_selector.h" ] + deps = [ + ":libjingle_peerconnection_api", + "../api/video_codecs:video_codecs_api", + "../test:test_support", + ] + } + rtc_library("fake_frame_encryptor") { visibility = [ "*" ] testonly = true @@ -973,6 +1173,7 @@ if (rtc_include_tests) { deps = [ ":array_view", ":libjingle_peerconnection_api", + ":make_ref_counted", ":rtp_parameters", "../rtc_base:checks", "../rtc_base:refcount", @@ -990,9 +1191,9 @@ if (rtc_include_tests) { deps = [ ":array_view", ":libjingle_peerconnection_api", + ":make_ref_counted", ":rtp_parameters", "../rtc_base:checks", - "../rtc_base:refcount", "crypto:frame_decryptor_interface", ] } @@ -1008,16 +1209,14 @@ if (rtc_include_tests) { ] } - rtc_source_set("dummy_peer_connection") { + rtc_source_set("mock_packet_socket_factory") { visibility = [ "*" ] testonly = true - sources = [ "test/dummy_peer_connection.h" ] + sources = [ "test/mock_packet_socket_factory.h" ] deps = [ - ":libjingle_peerconnection_api", - ":rtc_error", - "../rtc_base:checks", - "../rtc_base:refcount", + ":packet_socket_factory", + "../test:test_support", ] } @@ -1045,6 +1244,16 @@ if (rtc_include_tests) { ] } + rtc_source_set("mock_session_description_interface") { + visibility = [ "*" ] + testonly = true + sources = [ "test/mock_session_description_interface.h" ] + deps = [ + ":libjingle_peerconnection_api", + "../test:test_support", + ] + } + rtc_source_set("mock_async_dns_resolver") { visibility = [ "*" ] testonly = true @@ -1066,6 +1275,8 @@ if (rtc_include_tests) { deps = [ ":libjingle_peerconnection_api", + ":rtp_sender_interface", + "../api/crypto:frame_decryptor_interface", "../test:test_support", ] } @@ -1210,21 +1421,27 @@ if (rtc_include_tests) { "../rtc_base:rtc_event", "../rtc_base:rtc_task_queue", "../rtc_base:task_queue_for_test", + "../rtc_base/containers:flat_set", "../rtc_base/task_utils:repeating_task", "../system_wrappers:field_trial", + "../test:field_trial", "../test:fileutils", "../test:rtc_expect_death", "../test:test_support", "task_queue:task_queue_default_factory_unittests", + "test/pclf:media_configuration", + "test/video:video_frame_writer", "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" ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] } rtc_library("compile_all_headers") { @@ -1233,7 +1450,6 @@ if (rtc_include_tests) { sources = [ "test/compile_all_headers.cc" ] deps = [ - ":dummy_peer_connection", ":fake_frame_decryptor", ":fake_frame_encryptor", ":mock_async_dns_resolver", @@ -1244,9 +1460,11 @@ if (rtc_include_tests) { ":mock_frame_decryptor", ":mock_frame_encryptor", ":mock_media_stream_interface", + ":mock_packet_socket_factory", ":mock_peer_connection_factory_interface", ":mock_peerconnectioninterface", ":mock_rtp", + ":mock_session_description_interface", ":mock_transformable_video_frame", ":mock_video_bitrate_allocator", ":mock_video_bitrate_allocator_factory", @@ -1260,6 +1478,25 @@ if (rtc_include_tests) { } } +rtc_source_set("field_trials_registry") { + visibility = [ "*" ] + sources = [ + "field_trials_registry.cc", + "field_trials_registry.h", + ] + deps = [ + ":field_trials_view", + "../experiments:registered_field_trials", + "../rtc_base:checks", + "../rtc_base/containers:flat_set", + "../rtc_base/system:rtc_export", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + ] +} + rtc_source_set("field_trials_view") { visibility = [ "*" ] sources = [ "field_trials_view.h" ] @@ -1280,10 +1517,26 @@ rtc_library("field_trials") { "field_trials.h", ] deps = [ - ":field_trials_view", + ":field_trials_registry", "../rtc_base:checks", "../rtc_base/containers:flat_map", "../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } + +rtc_library("frame_transformer_factory") { + visibility = [ "*" ] + sources = [ + "frame_transformer_factory.cc", + "frame_transformer_factory.h", + ] + deps = [ + ":frame_transformer_interface", + ":scoped_refptr", + "../modules/rtp_rtcp", + "../rtc_base:refcount", + "video:encoded_frame", + "video:video_frame_metadata", + ] +} diff --git a/api/DEPS b/api/DEPS index 9db91bef37..bcfd705741 100644 --- a/api/DEPS +++ b/api/DEPS @@ -11,6 +11,7 @@ include_rules = [ "-common_video", "-data", "-examples", + "-experiments", "-g3doc", "-ios", "-infra", @@ -143,7 +144,6 @@ specific_include_rules = { "proxy\.h": [ "+rtc_base/event.h", "+rtc_base/message_handler.h", # Inherits from it. - "+rtc_base/ref_counted_object.h", "+rtc_base/thread.h", ], @@ -159,9 +159,6 @@ specific_include_rules = { # For private member and constructor. "+rtc_base/system/file_wrapper.h", ], - "rtp_packet_infos\.h": [ - "+rtc_base/ref_counted_object.h", - ], "rtp_receiver_interface\.h": [ "+rtc_base/ref_count.h", ], @@ -185,7 +182,7 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], - "stats_types\.h": [ + "legacy_stats_types\.h": [ "+rtc_base/ref_count.h", "+rtc_base/thread_checker.h", ], @@ -206,10 +203,6 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], - "audio_decoder_factory_template\.h": [ - "+rtc_base/ref_counted_object.h", - ], - "audio_encoder\.h": [ "+rtc_base/buffer.h", ], @@ -218,10 +211,6 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], - "audio_encoder_factory_template\.h": [ - "+rtc_base/ref_counted_object.h", - ], - "frame_decryptor_interface\.h": [ "+rtc_base/ref_count.h", ], @@ -236,7 +225,6 @@ specific_include_rules = { "rtc_stats_report\.h": [ "+rtc_base/ref_count.h", - "+rtc_base/ref_counted_object.h", ], "audioproc_float\.h": [ @@ -247,14 +235,6 @@ specific_include_rules = { "+modules/audio_processing/include/audio_processing.h", ], - "fake_frame_decryptor\.h": [ - "+rtc_base/ref_counted_object.h", - ], - - "fake_frame_encryptor\.h": [ - "+rtc_base/ref_counted_object.h", - ], - "fake_metronome\.h": [ "+rtc_base/synchronization/mutex.h", "+rtc_base/task_queue.h", @@ -262,6 +242,10 @@ specific_include_rules = { "+rtc_base/thread_annotations.h", ], + "make_ref_counted\.h": [ + "+rtc_base/ref_counted_object.h", + ], + "mock.*\.h": [ "+test/gmock.h", ], @@ -316,10 +300,22 @@ specific_include_rules = { "+modules/video_coding", ], + "video_decoder_factory_template.*\.h": [ + "+modules/video_coding", + ], + "field_trials\.h": [ "+rtc_base/containers/flat_map.h", ], + "video_track_source_proxy_factory.h": [ + "+rtc_base/thread.h", + ], + + "field_trials_registry\.h": [ + "+rtc_base/containers/flat_set.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 @@ -330,6 +326,7 @@ specific_include_rules = { "+common_audio", "+common_video", "+examples", + "+experiments", "+logging", "+media", "+modules", diff --git a/api/OWNERS b/api/OWNERS index 516ae17f5e..383ac8a3ed 100644 --- a/api/OWNERS +++ b/api/OWNERS @@ -1,11 +1,12 @@ -crodbro@webrtc.org -deadbeef@webrtc.org hta@webrtc.org magjed@webrtc.org perkj@webrtc.org -tkchin@webrtc.org tommi@webrtc.org +# For approvals that absolutely must be done on US Pacific time +deadbeef@webrtc.org +tkchin@webrtc.org + per-file peer_connection*=hbos@webrtc.org per-file DEPS=mbonadei@webrtc.org diff --git a/api/README.md b/api/README.md index 7c1a27f512..7153cb57c4 100644 --- a/api/README.md +++ b/api/README.md @@ -1,6 +1,9 @@ + + + # How to write code in the `api/` directory -Mostly, just follow the regular [style guide](../g3doc/style-guide.md), but: +Mostly, just follow the regular [style guide](/g3doc/style-guide.md), but: * Note that `api/` code is not exempt from the “`.h` and `.cc` files come in pairs” rule, so if you declare something in `api/path/to/foo.h`, it should be @@ -17,7 +20,7 @@ it from a `.cc` file, so that users of our API headers won’t transitively For headers in `api/` that need to refer to non-public types, forward declarations are often a lesser evil than including non-public header files. The -usual [rules](../g3doc/style-guide.md#forward-declarations) still apply, though. +usual [rules](/g3doc/style-guide.md#forward-declarations) still apply, though. `.cc` files in `api/` should preferably be kept reasonably small. If a substantial implementation is needed, consider putting it with our non-public diff --git a/api/async_dns_resolver.h b/api/async_dns_resolver.h index 138503b59f..82d80de2c3 100644 --- a/api/async_dns_resolver.h +++ b/api/async_dns_resolver.h @@ -14,6 +14,7 @@ #include #include +#include "rtc_base/checks.h" #include "rtc_base/socket_address.h" #include "rtc_base/system/rtc_export.h" @@ -63,6 +64,10 @@ class RTC_EXPORT AsyncDnsResolverInterface { // Start address resolution of the hostname in `addr`. virtual void Start(const rtc::SocketAddress& addr, std::function callback) = 0; + // Start address resolution of the hostname in `addr` matching `family`. + virtual void Start(const rtc::SocketAddress& addr, + int family, + std::function callback) = 0; virtual const AsyncDnsResolverResult& result() const = 0; }; @@ -79,6 +84,14 @@ class AsyncDnsResolverFactoryInterface { virtual std::unique_ptr CreateAndResolve( const rtc::SocketAddress& addr, std::function callback) = 0; + // Creates an AsyncDnsResolver and starts resolving the name to an address + // matching the specified family. The callback will be called when resolution + // is finished. The callback will be called on the sequence that the caller + // runs on. + virtual std::unique_ptr CreateAndResolve( + const rtc::SocketAddress& addr, + int family, + std::function callback) = 0; // Creates an AsyncDnsResolver and does not start it. // For backwards compatibility, will be deprecated and removed. // One has to do a separate Start() call on the diff --git a/api/audio/BUILD.gn b/api/audio/BUILD.gn index 7220106d3c..4832751b5f 100644 --- a/api/audio/BUILD.gn +++ b/api/audio/BUILD.gn @@ -37,6 +37,7 @@ rtc_source_set("audio_mixer_api") { deps = [ ":audio_frame_api", + "..:make_ref_counted", "../../rtc_base:refcount", ] } @@ -102,9 +103,9 @@ rtc_source_set("echo_detector_creator") { "echo_detector_creator.h", ] deps = [ + "..:make_ref_counted", "../../api:scoped_refptr", "../../modules/audio_processing:api", "../../modules/audio_processing:residual_echo_detector", - "../../rtc_base:refcount", ] } diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h index bc9718c3e2..4b1c7fbc47 100644 --- a/api/audio/echo_canceller3_config.h +++ b/api/audio/echo_canceller3_config.h @@ -59,6 +59,7 @@ struct RTC_EXPORT EchoCanceller3Config { }; AlignmentMixing render_alignment_mixing = {false, true, 10000.f, true}; AlignmentMixing capture_alignment_mixing = {false, true, 10000.f, false}; + bool detect_pre_echo = true; } delay; struct Filter { diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc index e41612eb14..96e45ffe6d 100644 --- a/api/audio/echo_canceller3_config_json.cc +++ b/api/audio/echo_canceller3_config_json.cc @@ -220,6 +220,7 @@ void Aec3ConfigFromJsonString(absl::string_view json_string, &cfg.delay.render_alignment_mixing); ReadParam(section, "capture_alignment_mixing", &cfg.delay.capture_alignment_mixing); + ReadParam(section, "detect_pre_echo", &cfg.delay.detect_pre_echo); } if (rtc::GetValueFromJsonObject(aec3_root, "filter", §ion)) { @@ -505,7 +506,9 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) { << (config.delay.capture_alignment_mixing.prefer_first_two_channels ? "true" : "false"); - ost << "}"; + ost << "},"; + ost << "\"detect_pre_echo\": " + << (config.delay.detect_pre_echo ? "true" : "false"); ost << "},"; ost << "\"filter\": {"; diff --git a/api/audio/echo_detector_creator.cc b/api/audio/echo_detector_creator.cc index 04215b0deb..15b7c51dca 100644 --- a/api/audio/echo_detector_creator.cc +++ b/api/audio/echo_detector_creator.cc @@ -9,8 +9,8 @@ */ #include "api/audio/echo_detector_creator.h" +#include "api/make_ref_counted.h" #include "modules/audio_processing/residual_echo_detector.h" -#include "rtc_base/ref_counted_object.h" namespace webrtc { diff --git a/api/audio_codecs/BUILD.gn b/api/audio_codecs/BUILD.gn index ec358e12e2..82ed31a5da 100644 --- a/api/audio_codecs/BUILD.gn +++ b/api/audio_codecs/BUILD.gn @@ -31,6 +31,7 @@ rtc_library("audio_codecs_api") { deps = [ "..:array_view", "..:bitrate_allocation", + "..:make_ref_counted", "..:scoped_refptr", "../../api:field_trials_view", "../../rtc_base:buffer", @@ -42,6 +43,7 @@ rtc_library("audio_codecs_api") { "../units:time_delta", ] absl_deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/types:optional", ] @@ -60,7 +62,6 @@ rtc_library("builtin_audio_decoder_factory") { "L16:audio_decoder_L16", "g711:audio_decoder_g711", "g722:audio_decoder_g722", - "isac:audio_decoder_isac", ] defines = [] if (rtc_include_ilbc) { @@ -93,7 +94,6 @@ rtc_library("builtin_audio_encoder_factory") { "L16:audio_encoder_L16", "g711:audio_encoder_g711", "g722:audio_encoder_g722", - "isac:audio_encoder_isac", ] defines = [] if (rtc_include_ilbc) { diff --git a/api/audio_codecs/OWNERS b/api/audio_codecs/OWNERS index 77e9d0022a..77b414abc3 100644 --- a/api/audio_codecs/OWNERS +++ b/api/audio_codecs/OWNERS @@ -1,2 +1,3 @@ -minyue@webrtc.org +alessiob@webrtc.org henrik.lundin@webrtc.org +jakobi@webrtc.org diff --git a/api/audio_codecs/audio_decoder_factory_template.h b/api/audio_codecs/audio_decoder_factory_template.h index 4e39365182..7ea0c91372 100644 --- a/api/audio_codecs/audio_decoder_factory_template.h +++ b/api/audio_codecs/audio_decoder_factory_template.h @@ -16,8 +16,8 @@ #include "api/audio_codecs/audio_decoder_factory.h" #include "api/field_trials_view.h" +#include "api/make_ref_counted.h" #include "api/scoped_refptr.h" -#include "rtc_base/ref_counted_object.h" namespace webrtc { diff --git a/api/audio_codecs/audio_encoder_factory_template.h b/api/audio_codecs/audio_encoder_factory_template.h index 8490f46b7d..8a70ba2268 100644 --- a/api/audio_codecs/audio_encoder_factory_template.h +++ b/api/audio_codecs/audio_encoder_factory_template.h @@ -16,8 +16,8 @@ #include "api/audio_codecs/audio_encoder_factory.h" #include "api/field_trials_view.h" +#include "api/make_ref_counted.h" #include "api/scoped_refptr.h" -#include "rtc_base/ref_counted_object.h" namespace webrtc { diff --git a/api/audio_codecs/builtin_audio_decoder_factory.cc b/api/audio_codecs/builtin_audio_decoder_factory.cc index 963cfe5cb9..881113d985 100644 --- a/api/audio_codecs/builtin_audio_decoder_factory.cc +++ b/api/audio_codecs/builtin_audio_decoder_factory.cc @@ -20,7 +20,6 @@ #if WEBRTC_USE_BUILTIN_ILBC #include "api/audio_codecs/ilbc/audio_decoder_ilbc.h" // nogncheck #endif -#include "api/audio_codecs/isac/audio_decoder_isac.h" #if WEBRTC_USE_BUILTIN_OPUS #include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h" #include "api/audio_codecs/opus/audio_decoder_opus.h" // nogncheck @@ -57,7 +56,7 @@ rtc::scoped_refptr CreateBuiltinAudioDecoderFactory() { AudioDecoderOpus, NotAdvertised, #endif - AudioDecoderIsac, AudioDecoderG722, + AudioDecoderG722, #if WEBRTC_USE_BUILTIN_ILBC AudioDecoderIlbc, diff --git a/api/audio_codecs/builtin_audio_encoder_factory.cc b/api/audio_codecs/builtin_audio_encoder_factory.cc index 530d64b2ba..4546a2eaee 100644 --- a/api/audio_codecs/builtin_audio_encoder_factory.cc +++ b/api/audio_codecs/builtin_audio_encoder_factory.cc @@ -20,7 +20,6 @@ #if WEBRTC_USE_BUILTIN_ILBC #include "api/audio_codecs/ilbc/audio_encoder_ilbc.h" // nogncheck #endif -#include "api/audio_codecs/isac/audio_encoder_isac.h" #if WEBRTC_USE_BUILTIN_OPUS #include "api/audio_codecs/opus/audio_encoder_multi_channel_opus.h" #include "api/audio_codecs/opus/audio_encoder_opus.h" // nogncheck @@ -63,7 +62,7 @@ rtc::scoped_refptr CreateBuiltinAudioEncoderFactory() { AudioEncoderOpus, NotAdvertised, #endif - AudioEncoderIsac, AudioEncoderG722, + AudioEncoderG722, #if WEBRTC_USE_BUILTIN_ILBC AudioEncoderIlbc, diff --git a/api/audio_codecs/g722/BUILD.gn b/api/audio_codecs/g722/BUILD.gn index d78ee75984..af13ac3de3 100644 --- a/api/audio_codecs/g722/BUILD.gn +++ b/api/audio_codecs/g722/BUILD.gn @@ -15,6 +15,7 @@ if (is_android) { rtc_source_set("audio_encoder_g722_config") { visibility = [ "*" ] sources = [ "audio_encoder_g722_config.h" ] + deps = [ "..:audio_codecs_api" ] } rtc_library("audio_encoder_g722") { diff --git a/api/audio_codecs/g722/audio_encoder_g722_config.h b/api/audio_codecs/g722/audio_encoder_g722_config.h index f85eef00a8..f3f3a9f016 100644 --- a/api/audio_codecs/g722/audio_encoder_g722_config.h +++ b/api/audio_codecs/g722/audio_encoder_g722_config.h @@ -11,6 +11,8 @@ #ifndef API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_ #define API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_ +#include "api/audio_codecs/audio_encoder.h" + namespace webrtc { struct AudioEncoderG722Config { diff --git a/api/audio_codecs/isac/BUILD.gn b/api/audio_codecs/isac/BUILD.gn deleted file mode 100644 index 96a0ed5013..0000000000 --- a/api/audio_codecs/isac/BUILD.gn +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. -# -# Use of this source code is governed by a BSD-style license -# that can be found in the LICENSE file in the root of the source -# tree. An additional intellectual property rights grant can be found -# in the file PATENTS. All contributing project authors may -# be found in the AUTHORS file in the root of the source tree. - -import("../../../webrtc.gni") -if (is_android) { - import("//build/config/android/config.gni") - import("//build/config/android/rules.gni") -} - -# The targets with _fix and _float suffixes unconditionally use the -# fixed-point and floating-point iSAC implementations, respectively. -# The targets without suffixes pick one of the implementations based -# on cleverly chosen criteria. - -rtc_source_set("audio_encoder_isac") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - public = [ "audio_encoder_isac.h" ] - public_configs = [ ":isac_config" ] - if (current_cpu == "arm") { - deps = [ ":audio_encoder_isac_fix" ] - } else { - deps = [ ":audio_encoder_isac_float" ] - } -} - -rtc_source_set("audio_decoder_isac") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - public = [ "audio_decoder_isac.h" ] - public_configs = [ ":isac_config" ] - if (current_cpu == "arm") { - deps = [ ":audio_decoder_isac_fix" ] - } else { - deps = [ ":audio_decoder_isac_float" ] - } -} - -config("isac_config") { - visibility = [ ":*" ] - if (current_cpu == "arm") { - defines = [ - "WEBRTC_USE_BUILTIN_ISAC_FIX=1", - "WEBRTC_USE_BUILTIN_ISAC_FLOAT=0", - ] - } else { - defines = [ - "WEBRTC_USE_BUILTIN_ISAC_FIX=0", - "WEBRTC_USE_BUILTIN_ISAC_FLOAT=1", - ] - } -} - -rtc_library("audio_encoder_isac_fix") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - sources = [ - "audio_encoder_isac_fix.cc", - "audio_encoder_isac_fix.h", - ] - deps = [ - "..:audio_codecs_api", - "../../../api:field_trials_view", - "../../../modules/audio_coding:isac_fix", - "../../../rtc_base:stringutils", - "../../../rtc_base/system:rtc_export", - ] - absl_deps = [ - "//third_party/abseil-cpp/absl/strings", - "//third_party/abseil-cpp/absl/types:optional", - ] -} - -rtc_library("audio_decoder_isac_fix") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - sources = [ - "audio_decoder_isac_fix.cc", - "audio_decoder_isac_fix.h", - ] - deps = [ - "..:audio_codecs_api", - "../../../api:field_trials_view", - "../../../modules/audio_coding:isac_fix", - "../../../rtc_base/system:rtc_export", - ] - absl_deps = [ - "//third_party/abseil-cpp/absl/strings", - "//third_party/abseil-cpp/absl/types:optional", - ] -} - -rtc_library("audio_encoder_isac_float") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - sources = [ - "audio_encoder_isac_float.cc", - "audio_encoder_isac_float.h", - ] - deps = [ - "..:audio_codecs_api", - "../../../api:field_trials_view", - "../../../modules/audio_coding:isac", - "../../../rtc_base:stringutils", - "../../../rtc_base/system:rtc_export", - ] - absl_deps = [ - "//third_party/abseil-cpp/absl/strings", - "//third_party/abseil-cpp/absl/types:optional", - ] -} - -rtc_library("audio_decoder_isac_float") { - visibility = [ "*" ] - poisonous = [ "audio_codecs" ] - sources = [ - "audio_decoder_isac_float.cc", - "audio_decoder_isac_float.h", - ] - deps = [ - "..:audio_codecs_api", - "../../../api:field_trials_view", - "../../../modules/audio_coding:isac", - "../../../rtc_base/system:rtc_export", - ] - absl_deps = [ - "//third_party/abseil-cpp/absl/strings", - "//third_party/abseil-cpp/absl/types:optional", - ] -} diff --git a/api/audio_codecs/isac/audio_decoder_isac.h b/api/audio_codecs/isac/audio_decoder_isac.h deleted file mode 100644 index f4e9331282..0000000000 --- a/api/audio_codecs/isac/audio_decoder_isac.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_H_ - -#if WEBRTC_USE_BUILTIN_ISAC_FIX && !WEBRTC_USE_BUILTIN_ISAC_FLOAT -#include "api/audio_codecs/isac/audio_decoder_isac_fix.h" // nogncheck -#elif WEBRTC_USE_BUILTIN_ISAC_FLOAT && !WEBRTC_USE_BUILTIN_ISAC_FIX -#include "api/audio_codecs/isac/audio_decoder_isac_float.h" // nogncheck -#else -#error "Must choose either fix or float" -#endif - -namespace webrtc { - -#if WEBRTC_USE_BUILTIN_ISAC_FIX -using AudioDecoderIsac = AudioDecoderIsacFix; -#elif WEBRTC_USE_BUILTIN_ISAC_FLOAT -using AudioDecoderIsac = AudioDecoderIsacFloat; -#endif - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_H_ diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.cc b/api/audio_codecs/isac/audio_decoder_isac_fix.cc deleted file mode 100644 index b3ab91da47..0000000000 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/audio_codecs/isac/audio_decoder_isac_fix.h" - -#include - -#include "absl/strings/match.h" -#include "modules/audio_coding/codecs/isac/fix/include/audio_decoder_isacfix.h" - -namespace webrtc { - -absl::optional AudioDecoderIsacFix::SdpToConfig( - const SdpAudioFormat& format) { - if (absl::EqualsIgnoreCase(format.name, "ISAC") && - format.clockrate_hz == 16000 && format.num_channels == 1) { - return Config(); - } - return absl::nullopt; -} - -void AudioDecoderIsacFix::AppendSupportedDecoders( - std::vector* specs) { - specs->push_back({{"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}}); -} - -std::unique_ptr AudioDecoderIsacFix::MakeAudioDecoder( - Config config, - absl::optional /*codec_pair_id*/, - const FieldTrialsView* field_trials) { - AudioDecoderIsacFixImpl::Config c; - c.sample_rate_hz = 16000; - return std::make_unique(c); -} - -} // namespace webrtc diff --git a/api/audio_codecs/isac/audio_decoder_isac_fix.h b/api/audio_codecs/isac/audio_decoder_isac_fix.h deleted file mode 100644 index 8f61d9ab0e..0000000000 --- a/api/audio_codecs/isac/audio_decoder_isac_fix.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FIX_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FIX_H_ - -#include -#include - -#include "absl/types/optional.h" -#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 { - -// iSAC decoder API (fixed-point implementation) for use as a template -// parameter to CreateAudioDecoderFactory<...>(). -struct RTC_EXPORT AudioDecoderIsacFix { - struct Config {}; // Empty---no config values needed! - static absl::optional SdpToConfig(const SdpAudioFormat& audio_format); - static void AppendSupportedDecoders(std::vector* specs); - static std::unique_ptr MakeAudioDecoder( - Config config, - absl::optional codec_pair_id = absl::nullopt, - const FieldTrialsView* field_trials = nullptr); -}; - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FIX_H_ diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.cc b/api/audio_codecs/isac/audio_decoder_isac_float.cc deleted file mode 100644 index 98f672b468..0000000000 --- a/api/audio_codecs/isac/audio_decoder_isac_float.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/audio_codecs/isac/audio_decoder_isac_float.h" - -#include - -#include "absl/strings/match.h" -#include "modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" - -namespace webrtc { - -absl::optional -AudioDecoderIsacFloat::SdpToConfig(const SdpAudioFormat& format) { - if (absl::EqualsIgnoreCase(format.name, "ISAC") && - (format.clockrate_hz == 16000 || format.clockrate_hz == 32000) && - format.num_channels == 1) { - Config config; - config.sample_rate_hz = format.clockrate_hz; - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return absl::nullopt; - } - return config; - } else { - return absl::nullopt; - } -} - -void AudioDecoderIsacFloat::AppendSupportedDecoders( - std::vector* specs) { - specs->push_back({{"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}}); - specs->push_back({{"ISAC", 32000, 1}, {32000, 1, 56000, 10000, 56000}}); -} - -std::unique_ptr AudioDecoderIsacFloat::MakeAudioDecoder( - Config config, - absl::optional /*codec_pair_id*/, - const FieldTrialsView* field_trials) { - AudioDecoderIsacFloatImpl::Config c; - c.sample_rate_hz = config.sample_rate_hz; - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return nullptr; - } - return std::make_unique(c); -} - -} // namespace webrtc diff --git a/api/audio_codecs/isac/audio_decoder_isac_float.h b/api/audio_codecs/isac/audio_decoder_isac_float.h deleted file mode 100644 index 864c6b999f..0000000000 --- a/api/audio_codecs/isac/audio_decoder_isac_float.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FLOAT_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FLOAT_H_ - -#include -#include - -#include "absl/types/optional.h" -#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 { - -// iSAC decoder API (floating-point implementation) for use as a template -// parameter to CreateAudioDecoderFactory<...>(). -struct RTC_EXPORT AudioDecoderIsacFloat { - struct Config { - bool IsOk() const { - return sample_rate_hz == 16000 || sample_rate_hz == 32000; - } - int sample_rate_hz = 16000; - }; - static absl::optional SdpToConfig(const SdpAudioFormat& audio_format); - static void AppendSupportedDecoders(std::vector* specs); - static std::unique_ptr MakeAudioDecoder( - Config config, - absl::optional codec_pair_id = absl::nullopt, - const FieldTrialsView* field_trials = nullptr); -}; - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_DECODER_ISAC_FLOAT_H_ diff --git a/api/audio_codecs/isac/audio_encoder_isac.h b/api/audio_codecs/isac/audio_encoder_isac.h deleted file mode 100644 index 3cb0a1f053..0000000000 --- a/api/audio_codecs/isac/audio_encoder_isac.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_H_ - -#if WEBRTC_USE_BUILTIN_ISAC_FIX && !WEBRTC_USE_BUILTIN_ISAC_FLOAT -#include "api/audio_codecs/isac/audio_encoder_isac_fix.h" // nogncheck -#elif WEBRTC_USE_BUILTIN_ISAC_FLOAT && !WEBRTC_USE_BUILTIN_ISAC_FIX -#include "api/audio_codecs/isac/audio_encoder_isac_float.h" // nogncheck -#else -#error "Must choose either fix or float" -#endif - -namespace webrtc { - -#if WEBRTC_USE_BUILTIN_ISAC_FIX -using AudioEncoderIsac = AudioEncoderIsacFix; -#elif WEBRTC_USE_BUILTIN_ISAC_FLOAT -using AudioEncoderIsac = AudioEncoderIsacFloat; -#endif - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_H_ diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.cc b/api/audio_codecs/isac/audio_encoder_isac_fix.cc deleted file mode 100644 index 39603775a4..0000000000 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.cc +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/audio_codecs/isac/audio_encoder_isac_fix.h" - -#include - -#include "absl/strings/match.h" -#include "modules/audio_coding/codecs/isac/fix/include/audio_encoder_isacfix.h" -#include "rtc_base/string_to_number.h" - -namespace webrtc { - -absl::optional AudioEncoderIsacFix::SdpToConfig( - const SdpAudioFormat& format) { - if (absl::EqualsIgnoreCase(format.name, "ISAC") && - format.clockrate_hz == 16000 && format.num_channels == 1) { - Config config; - const auto ptime_iter = format.parameters.find("ptime"); - if (ptime_iter != format.parameters.end()) { - const auto ptime = rtc::StringToNumber(ptime_iter->second); - if (ptime && *ptime >= 60) { - config.frame_size_ms = 60; - } - } - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return absl::nullopt; - } - return config; - } else { - return absl::nullopt; - } -} - -void AudioEncoderIsacFix::AppendSupportedEncoders( - std::vector* specs) { - const SdpAudioFormat fmt = {"ISAC", 16000, 1}; - const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt)); - specs->push_back({fmt, info}); -} - -AudioCodecInfo AudioEncoderIsacFix::QueryAudioEncoder( - AudioEncoderIsacFix::Config config) { - RTC_DCHECK(config.IsOk()); - return {16000, 1, 32000, 10000, 32000}; -} - -std::unique_ptr AudioEncoderIsacFix::MakeAudioEncoder( - AudioEncoderIsacFix::Config config, - int payload_type, - 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; - c.payload_type = payload_type; - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return nullptr; - } - return std::make_unique(c); -} - -} // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_fix.h b/api/audio_codecs/isac/audio_encoder_isac_fix.h deleted file mode 100644 index de0f1d1308..0000000000 --- a/api/audio_codecs/isac/audio_encoder_isac_fix.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FIX_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FIX_H_ - -#include -#include - -#include "absl/types/optional.h" -#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 { - -// iSAC encoder API (fixed-point implementation) for use as a template -// parameter to CreateAudioEncoderFactory<...>(). -struct RTC_EXPORT AudioEncoderIsacFix { - struct Config { - bool IsOk() const { - if (frame_size_ms != 30 && frame_size_ms != 60) { - return false; - } - if (bit_rate < 10000 || bit_rate > 32000) { - return false; - } - return true; - } - int frame_size_ms = 30; - int bit_rate = 32000; // Limit on short-term average bit rate, in bits/s. - }; - static absl::optional SdpToConfig(const SdpAudioFormat& audio_format); - static void AppendSupportedEncoders(std::vector* specs); - static AudioCodecInfo QueryAudioEncoder(Config config); - static std::unique_ptr MakeAudioEncoder( - Config config, - int payload_type, - absl::optional codec_pair_id = absl::nullopt, - const FieldTrialsView* field_trials = nullptr); -}; - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FIX_H_ diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.cc b/api/audio_codecs/isac/audio_encoder_isac_float.cc deleted file mode 100644 index e3e50080fa..0000000000 --- a/api/audio_codecs/isac/audio_encoder_isac_float.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/audio_codecs/isac/audio_encoder_isac_float.h" - -#include - -#include "absl/strings/match.h" -#include "modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h" -#include "rtc_base/string_to_number.h" - -namespace webrtc { - -absl::optional -AudioEncoderIsacFloat::SdpToConfig(const SdpAudioFormat& format) { - if (absl::EqualsIgnoreCase(format.name, "ISAC") && - (format.clockrate_hz == 16000 || format.clockrate_hz == 32000) && - format.num_channels == 1) { - Config config; - config.sample_rate_hz = format.clockrate_hz; - config.bit_rate = format.clockrate_hz == 16000 ? 32000 : 56000; - if (config.sample_rate_hz == 16000) { - // For sample rate 16 kHz, optionally use 60 ms frames, instead of the - // default 30 ms. - const auto ptime_iter = format.parameters.find("ptime"); - if (ptime_iter != format.parameters.end()) { - const auto ptime = rtc::StringToNumber(ptime_iter->second); - if (ptime && *ptime >= 60) { - config.frame_size_ms = 60; - } - } - } - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return absl::nullopt; - } - return config; - } else { - return absl::nullopt; - } -} - -void AudioEncoderIsacFloat::AppendSupportedEncoders( - std::vector* specs) { - for (int sample_rate_hz : {16000, 32000}) { - const SdpAudioFormat fmt = {"ISAC", sample_rate_hz, 1}; - const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt)); - specs->push_back({fmt, info}); - } -} - -AudioCodecInfo AudioEncoderIsacFloat::QueryAudioEncoder( - const AudioEncoderIsacFloat::Config& config) { - RTC_DCHECK(config.IsOk()); - constexpr int min_bitrate = 10000; - const int max_bitrate = config.sample_rate_hz == 16000 ? 32000 : 56000; - const int default_bitrate = max_bitrate; - return {config.sample_rate_hz, 1, default_bitrate, min_bitrate, max_bitrate}; -} - -std::unique_ptr AudioEncoderIsacFloat::MakeAudioEncoder( - const AudioEncoderIsacFloat::Config& config, - int payload_type, - 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; - c.frame_size_ms = config.frame_size_ms; - c.bit_rate = config.bit_rate; - if (!config.IsOk()) { - RTC_DCHECK_NOTREACHED(); - return nullptr; - } - return std::make_unique(c); -} - -} // namespace webrtc diff --git a/api/audio_codecs/isac/audio_encoder_isac_float.h b/api/audio_codecs/isac/audio_encoder_isac_float.h deleted file mode 100644 index d031d76db1..0000000000 --- a/api/audio_codecs/isac/audio_encoder_isac_float.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FLOAT_H_ -#define API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FLOAT_H_ - -#include -#include - -#include "absl/types/optional.h" -#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 { - -// iSAC encoder API (floating-point implementation) for use as a template -// parameter to CreateAudioEncoderFactory<...>(). -struct RTC_EXPORT AudioEncoderIsacFloat { - struct Config { - bool IsOk() const { - switch (sample_rate_hz) { - case 16000: - if (frame_size_ms != 30 && frame_size_ms != 60) { - return false; - } - if (bit_rate < 10000 || bit_rate > 32000) { - return false; - } - return true; - case 32000: - if (frame_size_ms != 30) { - return false; - } - if (bit_rate < 10000 || bit_rate > 56000) { - return false; - } - return true; - default: - return false; - } - } - int sample_rate_hz = 16000; - int frame_size_ms = 30; - int bit_rate = 32000; // Limit on short-term average bit rate, in bits/s. - }; - static absl::optional SdpToConfig(const SdpAudioFormat& audio_format); - static void AppendSupportedEncoders(std::vector* specs); - static AudioCodecInfo QueryAudioEncoder(const Config& config); - static std::unique_ptr MakeAudioEncoder( - const Config& config, - int payload_type, - absl::optional codec_pair_id = absl::nullopt, - const FieldTrialsView* field_trials = nullptr); -}; - -} // namespace webrtc - -#endif // API_AUDIO_CODECS_ISAC_AUDIO_ENCODER_ISAC_FLOAT_H_ diff --git a/api/audio_codecs/opus/BUILD.gn b/api/audio_codecs/opus/BUILD.gn index 2091cbd000..eb90a0b9ac 100644 --- a/api/audio_codecs/opus/BUILD.gn +++ b/api/audio_codecs/opus/BUILD.gn @@ -20,9 +20,7 @@ rtc_library("audio_encoder_opus_config") { "audio_encoder_opus_config.cc", "audio_encoder_opus_config.h", ] - deps = [ - "../../../rtc_base/system:rtc_export", - ] + deps = [ "../../../rtc_base/system:rtc_export" ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] defines = [] if (rtc_opus_variable_complexity) { @@ -35,6 +33,7 @@ rtc_library("audio_encoder_opus_config") { rtc_source_set("audio_decoder_opus_config") { visibility = [ "*" ] sources = [ "audio_decoder_multi_channel_opus_config.h" ] + deps = [ "..:audio_codecs_api" ] } rtc_library("audio_encoder_opus") { diff --git a/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h index 7350045bf5..f97c5c3193 100644 --- a/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h +++ b/api/audio_codecs/opus/audio_decoder_multi_channel_opus_config.h @@ -13,6 +13,8 @@ #include +#include "api/audio_codecs/audio_decoder.h" + namespace webrtc { struct AudioDecoderMultiChannelOpusConfig { // The number of channels that the decoder will output. diff --git a/api/audio_codecs/opus/audio_encoder_opus_config.cc b/api/audio_codecs/opus/audio_encoder_opus_config.cc index 0e6f55ee65..a9ab924b38 100644 --- a/api/audio_codecs/opus/audio_encoder_opus_config.cc +++ b/api/audio_codecs/opus/audio_encoder_opus_config.cc @@ -14,9 +14,7 @@ namespace webrtc { namespace { -#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM) -// If we are on Android, iOS and/or ARM, use a lower complexity setting by -// default, to save encoder complexity. +#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) constexpr int kDefaultComplexity = 5; #else constexpr int kDefaultComplexity = 9; diff --git a/api/audio_codecs/test/BUILD.gn b/api/audio_codecs/test/BUILD.gn index 12df649feb..89f5fef1ea 100644 --- a/api/audio_codecs/test/BUILD.gn +++ b/api/audio_codecs/test/BUILD.gn @@ -32,10 +32,6 @@ if (rtc_include_tests) { "../g722:audio_encoder_g722", "../ilbc:audio_decoder_ilbc", "../ilbc:audio_encoder_ilbc", - "../isac:audio_decoder_isac_fix", - "../isac:audio_decoder_isac_float", - "../isac:audio_encoder_isac_fix", - "../isac:audio_encoder_isac_float", "../opus:audio_decoder_opus", "../opus:audio_encoder_opus", ] 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 3662f3b76d..0b18cf934a 100644 --- a/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc @@ -16,8 +16,6 @@ #include "api/audio_codecs/g711/audio_decoder_g711.h" #include "api/audio_codecs/g722/audio_decoder_g722.h" #include "api/audio_codecs/ilbc/audio_decoder_ilbc.h" -#include "api/audio_codecs/isac/audio_decoder_isac_fix.h" -#include "api/audio_codecs/isac/audio_decoder_isac_float.h" #include "api/audio_codecs/opus/audio_decoder_opus.h" #include "test/gmock.h" #include "test/gtest.h" @@ -182,41 +180,6 @@ TEST(AudioDecoderFactoryTemplateTest, Ilbc) { EXPECT_EQ(8000, dec->SampleRateHz()); } -TEST(AudioDecoderFactoryTemplateTest, IsacFix) { - auto factory = CreateAudioDecoderFactory(); - EXPECT_THAT(factory->GetSupportedDecoders(), - ::testing::ElementsAre(AudioCodecSpec{ - {"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}})); - EXPECT_FALSE(factory->IsSupportedDecoder({"isac", 16000, 2})); - EXPECT_TRUE(factory->IsSupportedDecoder({"isac", 16000, 1})); - EXPECT_FALSE(factory->IsSupportedDecoder({"isac", 32000, 1})); - EXPECT_EQ(nullptr, - factory->MakeAudioDecoder({"isac", 8000, 1}, absl::nullopt)); - auto dec = factory->MakeAudioDecoder({"isac", 16000, 1}, absl::nullopt); - ASSERT_NE(nullptr, dec); - EXPECT_EQ(16000, dec->SampleRateHz()); -} - -TEST(AudioDecoderFactoryTemplateTest, IsacFloat) { - auto factory = CreateAudioDecoderFactory(); - EXPECT_THAT( - factory->GetSupportedDecoders(), - ::testing::ElementsAre( - AudioCodecSpec{{"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}}, - AudioCodecSpec{{"ISAC", 32000, 1}, {32000, 1, 56000, 10000, 56000}})); - EXPECT_FALSE(factory->IsSupportedDecoder({"isac", 16000, 2})); - EXPECT_TRUE(factory->IsSupportedDecoder({"isac", 16000, 1})); - EXPECT_TRUE(factory->IsSupportedDecoder({"isac", 32000, 1})); - EXPECT_EQ(nullptr, - factory->MakeAudioDecoder({"isac", 8000, 1}, absl::nullopt)); - auto dec1 = factory->MakeAudioDecoder({"isac", 16000, 1}, absl::nullopt); - ASSERT_NE(nullptr, dec1); - EXPECT_EQ(16000, dec1->SampleRateHz()); - auto dec2 = factory->MakeAudioDecoder({"isac", 32000, 1}, absl::nullopt); - ASSERT_NE(nullptr, dec2); - EXPECT_EQ(32000, dec2->SampleRateHz()); -} - TEST(AudioDecoderFactoryTemplateTest, L16) { auto factory = CreateAudioDecoderFactory(); EXPECT_THAT( 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 67b6883583..dbba387724 100644 --- a/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc +++ b/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc @@ -16,8 +16,6 @@ #include "api/audio_codecs/g711/audio_encoder_g711.h" #include "api/audio_codecs/g722/audio_encoder_g722.h" #include "api/audio_codecs/ilbc/audio_encoder_ilbc.h" -#include "api/audio_codecs/isac/audio_encoder_isac_fix.h" -#include "api/audio_codecs/isac/audio_encoder_isac_float.h" #include "api/audio_codecs/opus/audio_encoder_opus.h" #include "test/gmock.h" #include "test/gtest.h" @@ -180,49 +178,6 @@ TEST(AudioEncoderFactoryTemplateTest, Ilbc) { EXPECT_EQ(8000, enc->SampleRateHz()); } -TEST(AudioEncoderFactoryTemplateTest, IsacFix) { - auto factory = CreateAudioEncoderFactory(); - EXPECT_THAT(factory->GetSupportedEncoders(), - ::testing::ElementsAre(AudioCodecSpec{ - {"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}})); - EXPECT_EQ(absl::nullopt, factory->QueryAudioEncoder({"isac", 16000, 2})); - EXPECT_EQ(AudioCodecInfo(16000, 1, 32000, 10000, 32000), - factory->QueryAudioEncoder({"isac", 16000, 1})); - EXPECT_EQ(absl::nullopt, factory->QueryAudioEncoder({"isac", 32000, 1})); - EXPECT_EQ(nullptr, - factory->MakeAudioEncoder(17, {"isac", 8000, 1}, absl::nullopt)); - auto enc1 = factory->MakeAudioEncoder(17, {"isac", 16000, 1}, absl::nullopt); - ASSERT_NE(nullptr, enc1); - EXPECT_EQ(16000, enc1->SampleRateHz()); - EXPECT_EQ(3u, enc1->Num10MsFramesInNextPacket()); - auto enc2 = factory->MakeAudioEncoder( - 17, {"isac", 16000, 1, {{"ptime", "60"}}}, absl::nullopt); - ASSERT_NE(nullptr, enc2); - EXPECT_EQ(6u, enc2->Num10MsFramesInNextPacket()); -} - -TEST(AudioEncoderFactoryTemplateTest, IsacFloat) { - auto factory = CreateAudioEncoderFactory(); - EXPECT_THAT( - factory->GetSupportedEncoders(), - ::testing::ElementsAre( - AudioCodecSpec{{"ISAC", 16000, 1}, {16000, 1, 32000, 10000, 32000}}, - AudioCodecSpec{{"ISAC", 32000, 1}, {32000, 1, 56000, 10000, 56000}})); - EXPECT_EQ(absl::nullopt, factory->QueryAudioEncoder({"isac", 16000, 2})); - EXPECT_EQ(AudioCodecInfo(16000, 1, 32000, 10000, 32000), - factory->QueryAudioEncoder({"isac", 16000, 1})); - EXPECT_EQ(AudioCodecInfo(32000, 1, 56000, 10000, 56000), - factory->QueryAudioEncoder({"isac", 32000, 1})); - EXPECT_EQ(nullptr, - factory->MakeAudioEncoder(17, {"isac", 8000, 1}, absl::nullopt)); - auto enc1 = factory->MakeAudioEncoder(17, {"isac", 16000, 1}, absl::nullopt); - ASSERT_NE(nullptr, enc1); - EXPECT_EQ(16000, enc1->SampleRateHz()); - auto enc2 = factory->MakeAudioEncoder(17, {"isac", 32000, 1}, absl::nullopt); - ASSERT_NE(nullptr, enc2); - EXPECT_EQ(32000, enc2->SampleRateHz()); -} - TEST(AudioEncoderFactoryTemplateTest, L16) { auto factory = CreateAudioEncoderFactory(); EXPECT_THAT( diff --git a/api/audio_options.cc b/api/audio_options.cc index c301395f94..658515062c 100644 --- a/api/audio_options.cc +++ b/api/audio_options.cc @@ -52,8 +52,6 @@ void AudioOptions::SetAll(const AudioOptions& change) { change.audio_jitter_buffer_fast_accelerate); SetFrom(&audio_jitter_buffer_min_delay_ms, change.audio_jitter_buffer_min_delay_ms); - SetFrom(&audio_jitter_buffer_enable_rtx_handling, - change.audio_jitter_buffer_enable_rtx_handling); 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); @@ -74,8 +72,6 @@ bool AudioOptions::operator==(const AudioOptions& o) const { o.audio_jitter_buffer_fast_accelerate && audio_jitter_buffer_min_delay_ms == o.audio_jitter_buffer_min_delay_ms && - audio_jitter_buffer_enable_rtx_handling == - o.audio_jitter_buffer_enable_rtx_handling && 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 && @@ -101,8 +97,6 @@ std::string AudioOptions::ToString() const { audio_jitter_buffer_fast_accelerate); ToStringIfSet(&result, "audio_jitter_buffer_min_delay_ms", audio_jitter_buffer_min_delay_ms); - ToStringIfSet(&result, "audio_jitter_buffer_enable_rtx_handling", - audio_jitter_buffer_enable_rtx_handling); 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 1e4c820597..39ba3886ea 100644 --- a/api/audio_options.h +++ b/api/audio_options.h @@ -58,8 +58,6 @@ struct RTC_EXPORT AudioOptions { absl::optional audio_jitter_buffer_fast_accelerate; // Audio receiver jitter buffer (NetEq) minimum target delay in milliseconds. 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; // 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 76feb13fee..fec26593a6 100644 --- a/api/call/audio_sink.h +++ b/api/call/audio_sink.h @@ -11,6 +11,7 @@ #ifndef API_CALL_AUDIO_SINK_H_ #define API_CALL_AUDIO_SINK_H_ +#include #include namespace webrtc { diff --git a/api/candidate.cc b/api/candidate.cc index 4d17256c2e..a14dda350c 100644 --- a/api/candidate.cc +++ b/api/candidate.cc @@ -22,6 +22,7 @@ Candidate::Candidate() component_(0), priority_(0), network_type_(rtc::ADAPTER_TYPE_UNKNOWN), + underlying_type_for_vpn_(rtc::ADAPTER_TYPE_UNKNOWN), generation_(0), network_id_(0), network_cost_(0) {} @@ -46,6 +47,7 @@ Candidate::Candidate(int component, password_(password), type_(type), network_type_(rtc::ADAPTER_TYPE_UNKNOWN), + underlying_type_for_vpn_(rtc::ADAPTER_TYPE_UNKNOWN), generation_(generation), foundation_(foundation), network_id_(network_id), diff --git a/api/candidate.h b/api/candidate.h index b8aaebc14a..281f2f01a5 100644 --- a/api/candidate.h +++ b/api/candidate.h @@ -25,6 +25,10 @@ namespace cricket { +// TURN servers are limited to 32 in accordance with +// https://w3c.github.io/webrtc-pc/#dom-rtcconfiguration-iceservers +static constexpr size_t kMaxTurnServers = 32; + // Candidate for ICE based connection discovery. // TODO(phoglund): remove things in here that are not needed in the public API. diff --git a/api/create_peerconnection_factory.cc b/api/create_peerconnection_factory.cc index 4a01b2f3c9..f9cc7ad3e2 100644 --- a/api/create_peerconnection_factory.cc +++ b/api/create_peerconnection_factory.cc @@ -40,19 +40,20 @@ rtc::scoped_refptr CreatePeerConnectionFactory( rtc::scoped_refptr audio_processing, AudioFrameProcessor* audio_frame_processor, std::unique_ptr field_trials) { + if (!field_trials) { + field_trials = std::make_unique(); + } + PeerConnectionFactoryDependencies dependencies; dependencies.network_thread = network_thread; dependencies.worker_thread = worker_thread; dependencies.signaling_thread = signaling_thread; - dependencies.task_queue_factory = CreateDefaultTaskQueueFactory(); + dependencies.task_queue_factory = + CreateDefaultTaskQueueFactory(field_trials.get()); dependencies.call_factory = CreateCallFactory(); dependencies.event_log_factory = std::make_unique( dependencies.task_queue_factory.get()); - if (field_trials) { - dependencies.trials = std::move(field_trials); - } else { - dependencies.trials = std::make_unique(); - } + dependencies.trials = std::move(field_trials); if (network_thread) { // TODO(bugs.webrtc.org/13145): Add an rtc::SocketFactory* argument. diff --git a/api/crypto/BUILD.gn b/api/crypto/BUILD.gn index 70626f695f..8d041ea059 100644 --- a/api/crypto/BUILD.gn +++ b/api/crypto/BUILD.gn @@ -23,7 +23,7 @@ rtc_library("options") { "crypto_options.h", ] deps = [ - "../../rtc_base:rtc_base", + "../../rtc_base:ssl", "../../rtc_base/system:rtc_export", ] } diff --git a/api/crypto_params.h b/api/crypto_params.h index 5da352cbef..95bd892f9c 100644 --- a/api/crypto_params.h +++ b/api/crypto_params.h @@ -13,6 +13,8 @@ #include +#include "absl/strings/string_view.h" + namespace cricket { // Parameters for SRTP negotiation, as described in RFC 4568. @@ -21,9 +23,9 @@ namespace cricket { struct CryptoParams { CryptoParams() : tag(0) {} CryptoParams(int t, - const std::string& cs, - const std::string& kp, - const std::string& sp) + absl::string_view cs, + absl::string_view kp, + absl::string_view sp) : tag(t), cipher_suite(cs), key_params(kp), session_params(sp) {} bool Matches(const CryptoParams& params) const { diff --git a/api/field_trials.cc b/api/field_trials.cc index e6ca18b1cf..4bd11271dc 100644 --- a/api/field_trials.cc +++ b/api/field_trials.cc @@ -63,7 +63,8 @@ std::atomic instance_created_{false}; namespace webrtc { FieldTrials::FieldTrials(const std::string& s) - : field_trial_string_(s), + : uses_global_(true), + 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! @@ -72,13 +73,24 @@ FieldTrials::FieldTrials(const std::string& s) << "Only one instance may be instanciated at any given time!"; } +std::unique_ptr FieldTrials::CreateNoGlobal(const std::string& s) { + return std::unique_ptr(new FieldTrials(s, true)); +} + +FieldTrials::FieldTrials(const std::string& s, bool) + : uses_global_(false), + previous_field_trial_string_(nullptr), + key_value_map_(InsertIntoMap(s)) {} + FieldTrials::~FieldTrials() { // TODO(bugs.webrtc.org/10335): Remove the global string! - field_trial::InitFieldTrialsFromString(previous_field_trial_string_); - RTC_CHECK(instance_created_.exchange(false)); + if (uses_global_) { + field_trial::InitFieldTrialsFromString(previous_field_trial_string_); + RTC_CHECK(instance_created_.exchange(false)); + } } -std::string FieldTrials::Lookup(absl::string_view key) const { +std::string FieldTrials::GetValue(absl::string_view key) const { auto it = key_value_map_.find(std::string(key)); if (it != key_value_map_.end()) return it->second; @@ -86,7 +98,10 @@ std::string FieldTrials::Lookup(absl::string_view key) const { // 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)); + if (uses_global_) { + return field_trial::FindFullName(std::string(key)); + } + return ""; } } // namespace webrtc diff --git a/api/field_trials.h b/api/field_trials.h index 822e78c096..bf7a7cc625 100644 --- a/api/field_trials.h +++ b/api/field_trials.h @@ -11,10 +11,11 @@ #ifndef API_FIELD_TRIALS_H_ #define API_FIELD_TRIALS_H_ +#include #include #include "absl/strings/string_view.h" -#include "api/field_trials_view.h" +#include "api/field_trials_registry.h" #include "rtc_base/containers/flat_map.h" namespace webrtc { @@ -32,14 +33,22 @@ namespace webrtc { // // NOTE: Creating multiple FieldTrials-object is currently prohibited // until we remove the global string (TODO(bugs.webrtc.org/10335)) -class FieldTrials : public FieldTrialsView { +// (unless using CreateNoGlobal): +class FieldTrials : public FieldTrialsRegistry { public: explicit FieldTrials(const std::string& s); ~FieldTrials(); - std::string Lookup(absl::string_view key) const override; + // Create a FieldTrials object that is not reading/writing from + // global variable (i.e can not be used for all parts of webrtc). + static std::unique_ptr CreateNoGlobal(const std::string& s); private: + explicit FieldTrials(const std::string& s, bool); + + std::string GetValue(absl::string_view key) const override; + + const bool uses_global_; const std::string field_trial_string_; const char* const previous_field_trial_string_; const flat_map key_value_map_; diff --git a/api/field_trials_registry.cc b/api/field_trials_registry.cc new file mode 100644 index 0000000000..f97e8a85a9 --- /dev/null +++ b/api/field_trials_registry.cc @@ -0,0 +1,31 @@ +/* + * 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_registry.h" + +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/string_view.h" +#include "experiments/registered_field_trials.h" +#include "rtc_base/checks.h" +#include "rtc_base/containers/flat_set.h" + +namespace webrtc { + +std::string FieldTrialsRegistry::Lookup(absl::string_view key) const { +#if WEBRTC_STRICT_FIELD_TRIALS + RTC_DCHECK(absl::c_linear_search(kRegisteredFieldTrials, key) || + test_keys_.contains(key)) + << key << " is not registered."; +#endif + return GetValue(key); +} + +} // namespace webrtc diff --git a/api/field_trials_registry.h b/api/field_trials_registry.h new file mode 100644 index 0000000000..dc7e8445b1 --- /dev/null +++ b/api/field_trials_registry.h @@ -0,0 +1,54 @@ +/* + * 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_FIELD_TRIALS_REGISTRY_H_ +#define API_FIELD_TRIALS_REGISTRY_H_ + +#include +#include + +#include "absl/strings/string_view.h" +#include "api/field_trials_view.h" +#include "rtc_base/containers/flat_set.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// Abstract base class for a field trial registry that verifies that any looked +// up key has been pre-registered in accordance with `g3doc/field-trials.md`. +class RTC_EXPORT FieldTrialsRegistry : public FieldTrialsView { + public: + FieldTrialsRegistry() = default; + + FieldTrialsRegistry(const FieldTrialsRegistry&) = default; + FieldTrialsRegistry& operator=(const FieldTrialsRegistry&) = default; + + ~FieldTrialsRegistry() override = default; + + // Verifies that `key` is a registered field trial and then returns the + // configured value for `key` or an empty string if the field trial isn't + // configured. + std::string Lookup(absl::string_view key) const override; + + // Register additional `keys` for testing. This should only be used for + // imaginary keys that are never used outside test code. + void RegisterKeysForTesting(flat_set keys) { + test_keys_ = std::move(keys); + } + + private: + virtual std::string GetValue(absl::string_view key) const = 0; + + // Imaginary keys only used for testing. + flat_set test_keys_; +}; + +} // namespace webrtc + +#endif // API_FIELD_TRIALS_REGISTRY_H_ diff --git a/api/field_trials_unittest.cc b/api/field_trials_unittest.cc index fb2fff0db7..804b52a818 100644 --- a/api/field_trials_unittest.cc +++ b/api/field_trials_unittest.cc @@ -10,8 +10,15 @@ #include "api/field_trials.h" +#include +#include + +#include "absl/strings/string_view.h" #include "api/transport/field_trial_based_config.h" +#include "rtc_base/containers/flat_set.h" #include "system_wrappers/include/field_trial.h" +#include "test/field_trial.h" +#include "test/gmock.h" #include "test/gtest.h" #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) @@ -19,55 +26,127 @@ #endif // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) namespace webrtc { +namespace { + +using ::testing::NotNull; +using ::webrtc::field_trial::FieldTrialsAllowedInScopeForTesting; +using ::webrtc::test::ScopedFieldTrials; -TEST(FieldTrials, EmptyString) { +TEST(FieldTrialsTest, EmptyStringHasNoEffect) { + FieldTrialsAllowedInScopeForTesting k({"MyCoolTrial"}); FieldTrials f(""); + f.RegisterKeysForTesting({"MyCoolTrial"}); + EXPECT_FALSE(f.IsEnabled("MyCoolTrial")); EXPECT_FALSE(f.IsDisabled("MyCoolTrial")); } -TEST(FieldTrials, EnableDisable) { - FieldTrials f("MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"); +TEST(FieldTrialsTest, EnabledDisabledMustBeFirstInValue) { + FieldTrials f( + "MyCoolTrial/EnabledFoo/" + "MyUncoolTrial/DisabledBar/" + "AnotherTrial/BazEnabled/"); + f.RegisterKeysForTesting({"MyCoolTrial", "MyUncoolTrial", "AnotherTrial"}); + EXPECT_TRUE(f.IsEnabled("MyCoolTrial")); EXPECT_TRUE(f.IsDisabled("MyUncoolTrial")); + EXPECT_FALSE(f.IsEnabled("AnotherTrial")); } -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(FieldTrialsTest, FieldTrialsDoesNotReadGlobalString) { + FieldTrialsAllowedInScopeForTesting k({"MyCoolTrial", "MyUncoolTrial"}); + ScopedFieldTrials g("MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"); + FieldTrials f(""); + f.RegisterKeysForTesting({"MyCoolTrial", "MyUncoolTrial"}); + + EXPECT_FALSE(f.IsEnabled("MyCoolTrial")); + EXPECT_FALSE(f.IsDisabled("MyUncoolTrial")); } -TEST(FieldTrials, SetFieldTrialAndReadFromGlobalString) { +TEST(FieldTrialsTest, FieldTrialsWritesGlobalString) { + FieldTrialsAllowedInScopeForTesting k({"MyCoolTrial", "MyUncoolTrial"}); 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); +TEST(FieldTrialsTest, FieldTrialsRestoresGlobalStringAfterDestruction) { + static constexpr char s[] = "SomeString/Enabled/"; + ScopedFieldTrials g(s); { FieldTrials f("SomeOtherString/Enabled/"); - EXPECT_EQ(std::string("SomeOtherString/Enabled/"), - webrtc::field_trial::GetFieldTrialString()); + EXPECT_STREQ(webrtc::field_trial::GetFieldTrialString(), + "SomeOtherString/Enabled/"); } - EXPECT_EQ(s, webrtc::field_trial::GetFieldTrialString()); + EXPECT_STREQ(webrtc::field_trial::GetFieldTrialString(), s); } #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) -TEST(FieldTrials, OnlyOneInstance) { +TEST(FieldTrialsTest, FieldTrialsDoesNotSupportSimultaneousInstances) { 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) { +TEST(FieldTrialsTest, FieldTrialsSupportsSeparateInstances) { { FieldTrials f("SomeString/Enabled/"); } { FieldTrials f("SomeOtherString/Enabled/"); } } +TEST(FieldTrialsTest, NonGlobalFieldTrialsInstanceDoesNotModifyGlobalString) { + FieldTrialsAllowedInScopeForTesting k({"SomeString"}); + std::unique_ptr f = + FieldTrials::CreateNoGlobal("SomeString/Enabled/"); + ASSERT_THAT(f, NotNull()); + f->RegisterKeysForTesting({"SomeString"}); + + EXPECT_TRUE(f->IsEnabled("SomeString")); + EXPECT_FALSE(webrtc::field_trial::IsEnabled("SomeString")); +} + +TEST(FieldTrialsTest, NonGlobalFieldTrialsSupportSimultaneousInstances) { + std::unique_ptr f1 = + FieldTrials::CreateNoGlobal("SomeString/Enabled/"); + std::unique_ptr f2 = + FieldTrials::CreateNoGlobal("SomeOtherString/Enabled/"); + ASSERT_THAT(f1, NotNull()); + ASSERT_THAT(f2, NotNull()); + f1->RegisterKeysForTesting({"SomeString", "SomeOtherString"}); + f2->RegisterKeysForTesting({"SomeString", "SomeOtherString"}); + + EXPECT_TRUE(f1->IsEnabled("SomeString")); + EXPECT_FALSE(f1->IsEnabled("SomeOtherString")); + + EXPECT_FALSE(f2->IsEnabled("SomeString")); + EXPECT_TRUE(f2->IsEnabled("SomeOtherString")); +} + +TEST(FieldTrialsTest, GlobalAndNonGlobalFieldTrialsAreDisjoint) { + FieldTrialsAllowedInScopeForTesting k({"SomeString", "SomeOtherString"}); + FieldTrials f1("SomeString/Enabled/"); + std::unique_ptr f2 = + FieldTrials::CreateNoGlobal("SomeOtherString/Enabled/"); + ASSERT_THAT(f2, NotNull()); + f1.RegisterKeysForTesting({"SomeString", "SomeOtherString"}); + f2->RegisterKeysForTesting({"SomeString", "SomeOtherString"}); + + EXPECT_TRUE(f1.IsEnabled("SomeString")); + EXPECT_FALSE(f1.IsEnabled("SomeOtherString")); + + EXPECT_FALSE(f2->IsEnabled("SomeString")); + EXPECT_TRUE(f2->IsEnabled("SomeOtherString")); +} + +TEST(FieldTrialsTest, FieldTrialBasedConfigReadsGlobalString) { + FieldTrialsAllowedInScopeForTesting k({"MyCoolTrial", "MyUncoolTrial"}); + ScopedFieldTrials g("MyCoolTrial/Enabled/MyUncoolTrial/Disabled/"); + FieldTrialBasedConfig f; + f.RegisterKeysForTesting({"MyCoolTrial", "MyUncoolTrial"}); + + EXPECT_TRUE(f.IsEnabled("MyCoolTrial")); + EXPECT_TRUE(f.IsDisabled("MyUncoolTrial")); +} + +} // namespace } // namespace webrtc diff --git a/api/field_trials_view.h b/api/field_trials_view.h index 299205d1d3..45e6f7899b 100644 --- a/api/field_trials_view.h +++ b/api/field_trials_view.h @@ -17,15 +17,18 @@ namespace webrtc { -// An interface that provides a key-value mapping for configuring internal -// details of WebRTC. Note that there's no guarantess that the meaning of a -// 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. +// An interface that provides the means to access field trials. +// +// Note that there are no guarantess that the meaning of a 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 FieldTrialsView { public: virtual ~FieldTrialsView() = default; - // The configured value for the given key. Defaults to an empty string. + + // Returns the configured value for `key` or an empty string if the field + // trial isn't configured. virtual std::string Lookup(absl::string_view key) const = 0; bool IsEnabled(absl::string_view key) const { diff --git a/api/frame_transformer_factory.cc b/api/frame_transformer_factory.cc new file mode 100644 index 0000000000..af08372e37 --- /dev/null +++ b/api/frame_transformer_factory.cc @@ -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. + */ + +#include "api/frame_transformer_factory.h" + +#include "modules/rtp_rtcp/source/rtp_sender_video_frame_transformer_delegate.h" + +namespace webrtc { + +std::unique_ptr CreateVideoSenderFrame() { + RTC_CHECK_NOTREACHED(); + return nullptr; +} + +std::unique_ptr CreateVideoReceiverFrame() { + RTC_CHECK_NOTREACHED(); + return nullptr; +} + +std::unique_ptr CloneVideoFrame( + TransformableVideoFrameInterface* original) { + // At the moment, only making sender frames from receiver frames is supported. + return CloneSenderVideoFrame(original); +} + +} // namespace webrtc diff --git a/api/frame_transformer_factory.h b/api/frame_transformer_factory.h new file mode 100644 index 0000000000..8ba9c292d5 --- /dev/null +++ b/api/frame_transformer_factory.h @@ -0,0 +1,39 @@ +/* + * 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_FRAME_TRANSFORMER_FACTORY_H_ +#define API_FRAME_TRANSFORMER_FACTORY_H_ + +#include +#include + +#include "api/frame_transformer_interface.h" +#include "api/scoped_refptr.h" +#include "api/video/encoded_frame.h" +#include "api/video/video_frame_metadata.h" + +// This file contains EXPERIMENTAL functions to create video frames from +// either an old video frame or directly from parameters. +// These functions will be used in Chrome functionality to manipulate +// encoded frames from Javascript. +namespace webrtc { + +// TODO(bugs.webrtc.org/14708): Add the required parameters to these APIs. +std::unique_ptr CreateVideoSenderFrame(); +// TODO(bugs.webrtc.org/14708): Consider whether Receiver frames ever make sense +// to create. +std::unique_ptr CreateVideoReceiverFrame(); +// Creates a new frame with the same metadata as the original. +// The original can be a sender or receiver frame. +RTC_EXPORT std::unique_ptr CloneVideoFrame( + TransformableVideoFrameInterface* original); +} // namespace webrtc + +#endif // API_FRAME_TRANSFORMER_FACTORY_H_ diff --git a/api/frame_transformer_interface.h b/api/frame_transformer_interface.h index de2c612ac0..16a869e83b 100644 --- a/api/frame_transformer_interface.h +++ b/api/frame_transformer_interface.h @@ -36,6 +36,11 @@ class TransformableFrameInterface { virtual uint8_t GetPayloadType() const = 0; virtual uint32_t GetSsrc() const = 0; virtual uint32_t GetTimestamp() const = 0; + // TODO(https://bugs.webrtc.org/14878): Change this to pure virtual after it + // is implemented everywhere. + virtual absl::optional GetCaptureTimeIdentifier() const { + return absl::nullopt; + } enum class Direction { kUnknown, @@ -57,11 +62,14 @@ class TransformableVideoFrameInterface : public TransformableFrameInterface { // when the transformation applied to the frame is encryption/decryption, the // additional data holds the serialized generic frame descriptor extension // calculated in webrtc::RtpDescriptorAuthentication. - // TODO(bugs.webrtc.org/11380) remove from interface once - // webrtc::RtpDescriptorAuthentication is exposed in api/. - virtual std::vector GetAdditionalData() const = 0; + // This has been superseeded by GetMetadata() and will be removed shortly. + [[deprecated("https://crbug.com/1414370")]] virtual std::vector + GetAdditionalData() const = 0; virtual const VideoFrameMetadata& GetMetadata() const = 0; + // TODO(https://crbug.com/webrtc/14709): Make pure virtual when Chromium MOCK + // has implemented this. + virtual void SetMetadata(const VideoFrameMetadata&) {} }; // Extends the TransformableFrameInterface to expose audio-specific information. @@ -73,6 +81,8 @@ class TransformableAudioFrameInterface : public TransformableFrameInterface { // information in the header as needed, for example to compile the list of // csrcs. virtual const RTPHeader& GetHeader() const = 0; + + virtual rtc::ArrayView GetContributingSources() const = 0; }; // Objects implement this interface to be notified with the transformed frame. diff --git a/api/g3doc/index.md b/api/g3doc/index.md index 0c7136c039..b576514d5e 100644 --- a/api/g3doc/index.md +++ b/api/g3doc/index.md @@ -1,5 +1,5 @@ - - + + # The WebRTC API diff --git a/api/g3doc/threading_design.md b/api/g3doc/threading_design.md index 20c3539b22..8023b5eda0 100644 --- a/api/g3doc/threading_design.md +++ b/api/g3doc/threading_design.md @@ -1,5 +1,6 @@ - - + + + # API Threading Design considerations The header files in this directory form the API to the WebRTC library diff --git a/api/ice_transport_factory.cc b/api/ice_transport_factory.cc index 9e7e629a6f..e88ac183fa 100644 --- a/api/ice_transport_factory.cc +++ b/api/ice_transport_factory.cc @@ -13,6 +13,7 @@ #include #include +#include "api/make_ref_counted.h" #include "p2p/base/ice_transport_internal.h" #include "p2p/base/p2p_constants.h" #include "p2p/base/p2p_transport_channel.h" diff --git a/api/ice_transport_interface.h b/api/ice_transport_interface.h index 9d8bd5abe3..2ec41aaa69 100644 --- a/api/ice_transport_interface.h +++ b/api/ice_transport_interface.h @@ -24,6 +24,7 @@ namespace cricket { class IceTransportInternal; class PortAllocator; class IceControllerFactoryInterface; +class ActiveIceControllerFactoryInterface; } // namespace cricket namespace webrtc { @@ -84,6 +85,34 @@ struct IceTransportInit final { return ice_controller_factory_; } + // An active ICE controller actively manages the connection used by an ICE + // transport, in contrast with a legacy ICE controller that only picks the + // best connection to use or ping, and lets the transport decide when and + // whether to switch. + // + // Which ICE controller is used is determined based on the field trial + // "WebRTC-UseActiveIceController" as follows: + // + // 1. If the field trial is not enabled + // a. The legacy ICE controller factory is used if one is supplied. + // b. If not, a default ICE controller (BasicIceController) is + // constructed and used. + // + // 2. If the field trial is enabled + // a. If an active ICE controller factory is supplied, it is used and + // the legacy ICE controller factory is not used. + // b. If not, a default active ICE controller is used, wrapping over the + // supplied or the default legacy ICE controller. + void set_active_ice_controller_factory( + cricket::ActiveIceControllerFactoryInterface* + active_ice_controller_factory) { + active_ice_controller_factory_ = active_ice_controller_factory; + } + cricket::ActiveIceControllerFactoryInterface* + active_ice_controller_factory() { + return active_ice_controller_factory_; + } + const FieldTrialsView* field_trials() { return field_trials_; } void set_field_trials(const FieldTrialsView* field_trials) { field_trials_ = field_trials; @@ -96,6 +125,8 @@ struct IceTransportInit final { AsyncResolverFactory* async_resolver_factory_ = nullptr; RtcEventLog* event_log_ = nullptr; cricket::IceControllerFactoryInterface* ice_controller_factory_ = nullptr; + cricket::ActiveIceControllerFactoryInterface* active_ice_controller_factory_ = + nullptr; const FieldTrialsView* field_trials_ = nullptr; // TODO(https://crbug.com/webrtc/12657): Redesign to have const members. }; diff --git a/api/stats_types.cc b/api/legacy_stats_types.cc similarity index 99% rename from api/stats_types.cc rename to api/legacy_stats_types.cc index 8f69e5f876..e3b2144edd 100644 --- a/api/stats_types.cc +++ b/api/legacy_stats_types.cc @@ -8,13 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "api/stats_types.h" +#include "api/legacy_stats_types.h" #include #include "absl/algorithm/container.h" +#include "api/make_ref_counted.h" #include "rtc_base/checks.h" -#include "rtc_base/ref_counted_object.h" #include "rtc_base/string_encode.h" // TODO(tommi): Could we have a static map of value name -> expected type diff --git a/api/stats_types.h b/api/legacy_stats_types.h similarity index 99% rename from api/stats_types.h rename to api/legacy_stats_types.h index d75da46439..a62e014834 100644 --- a/api/stats_types.h +++ b/api/legacy_stats_types.h @@ -11,8 +11,8 @@ // This file contains structures used for retrieving statistics from an ongoing // libjingle session. -#ifndef API_STATS_TYPES_H_ -#define API_STATS_TYPES_H_ +#ifndef API_LEGACY_STATS_TYPES_H_ +#define API_LEGACY_STATS_TYPES_H_ #include #include @@ -452,4 +452,4 @@ class StatsCollection { } // namespace webrtc -#endif // API_STATS_TYPES_H_ +#endif // API_LEGACY_STATS_TYPES_H_ diff --git a/api/make_ref_counted.h b/api/make_ref_counted.h new file mode 100644 index 0000000000..cc8871784a --- /dev/null +++ b/api/make_ref_counted.h @@ -0,0 +1,119 @@ +/* + * 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_MAKE_REF_COUNTED_H_ +#define API_MAKE_REF_COUNTED_H_ + +#include + +#include "rtc_base/ref_counted_object.h" + +namespace rtc { + +namespace webrtc_make_ref_counted_internal { +// Determines if the given class has AddRef and Release methods. +template +class HasAddRefAndRelease { + private: + template ().AddRef())* = nullptr, + decltype(std::declval().Release())* = nullptr> + static int Test(int); + template + static char Test(...); + + public: + static constexpr bool value = std::is_same_v(0)), int>; +}; +} // namespace webrtc_make_ref_counted_internal + +// General utilities for constructing a reference counted class and the +// appropriate reference count implementation for that class. +// +// These utilities select either the `RefCountedObject` implementation or +// `FinalRefCountedObject` depending on whether the to-be-shared class is +// derived from the RefCountInterface interface or not (respectively). + +// `make_ref_counted`: +// +// Use this when you want to construct a reference counted object of type T and +// get a `scoped_refptr<>` back. Example: +// +// auto p = make_ref_counted("bar", 123); +// +// For a class that inherits from RefCountInterface, this is equivalent to: +// +// auto p = scoped_refptr(new RefCountedObject("bar", 123)); +// +// If the class does not inherit from RefCountInterface, but does have +// AddRef/Release methods (so a T* is convertible to rtc::scoped_refptr), this +// is equivalent to just +// +// auto p = scoped_refptr(new Foo("bar", 123)); +// +// Otherwise, the example is equivalent to: +// +// auto p = scoped_refptr>( +// new FinalRefCountedObject("bar", 123)); +// +// In these cases, `make_ref_counted` reduces the amount of boilerplate code but +// also helps with the most commonly intended usage of RefCountedObject whereby +// methods for reference counting, are virtual and designed to satisfy the need +// of an interface. When such a need does not exist, it is more efficient to use +// the `FinalRefCountedObject` template, which does not add the vtable overhead. +// +// Note that in some cases, using RefCountedObject directly may still be what's +// needed. + +// `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)...)); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface and already carry a ref count. +template < + typename T, + typename... Args, + typename std::enable_if< + !std::is_convertible_v && + webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + T>::type* = nullptr> +scoped_refptr make_ref_counted(Args&&... args) { + return scoped_refptr(new T(std::forward(args)...)); +} + +// `make_ref_counted` for complete classes that are not convertible to +// RefCountInterface and have no ref count of their own. +template < + typename T, + typename... Args, + typename std::enable_if< + !std::is_convertible_v && + !webrtc_make_ref_counted_internal::HasAddRefAndRelease::value, + + T>::type* = nullptr> +scoped_refptr> make_ref_counted(Args&&... args) { + return scoped_refptr>( + new FinalRefCountedObject(std::forward(args)...)); +} + +} // namespace rtc + +#endif // API_MAKE_REF_COUNTED_H_ diff --git a/api/media_stream_track.h b/api/media_stream_track.h index 738f034143..316dd788ef 100644 --- a/api/media_stream_track.h +++ b/api/media_stream_track.h @@ -13,6 +13,7 @@ #include +#include "absl/strings/string_view.h" #include "api/media_stream_interface.h" #include "api/notifier.h" @@ -41,7 +42,7 @@ class MediaStreamTrack : public Notifier { void set_ended() { set_state(MediaStreamTrackInterface::TrackState::kEnded); } protected: - explicit MediaStreamTrack(const std::string& id) + explicit MediaStreamTrack(absl::string_view id) : enabled_(true), id_(id), state_(MediaStreamTrackInterface::kLive) {} bool set_state(MediaStreamTrackInterface::TrackState new_state) { diff --git a/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc b/api/metronome/metronome.cc similarity index 57% rename from modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc rename to api/metronome/metronome.cc index b671002e1e..8d74f928a0 100644 --- a/modules/audio_coding/codecs/isac/main/source/audio_decoder_isac.cc +++ b/api/metronome/metronome.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 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 @@ -8,13 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h" - -#include "modules/audio_coding/codecs/isac/audio_decoder_isac_t_impl.h" +#include "api/metronome/metronome.h" namespace webrtc { -// Explicit instantiation: -template class AudioDecoderIsacT; +// TODO(crbug.com/1381982): Remove outdated methods. +void Metronome::AddListener(TickListener* listener) {} +void Metronome::RemoveListener(TickListener* listener) {} } // namespace webrtc diff --git a/api/metronome/metronome.h b/api/metronome/metronome.h index fc5f350db2..a312b1c862 100644 --- a/api/metronome/metronome.h +++ b/api/metronome/metronome.h @@ -17,44 +17,26 @@ namespace webrtc { -// The Metronome posts OnTick() on task queues provided by its listeners' task -// queue periodically. The metronome can be used as an alternative to using -// PostDelayedTask on a thread or task queue for coalescing work and reducing -// the number of idle-wakeups. -// -// Listeners can be added and removed from any sequence, but it is illegal to -// remove a listener from an OnTick invocation. +// The Metronome posts OnTick() calls requested with RequestCallOnNextTick. +// The API is designed to be fully used from a single task queue. Scheduled +// callbacks are executed on the same sequence as they were requested on. There +// are no features implemented for cancellation. When that's needed, use e.g. +// ScopedTaskSafety from the client. // // The metronome concept is still under experimentation, and may not be availble // in all platforms or applications. See https://crbug.com/1253787 for more // details. // -// Metronome implementations must be thread-safe. +// Metronome implementations must be thread-compatible. class RTC_EXPORT Metronome { public: - class RTC_EXPORT TickListener { - public: - virtual ~TickListener() = default; - - // OnTick is run on the task queue provided by OnTickTaskQueue each time the - // metronome ticks. - virtual void OnTick() = 0; - - // The task queue that OnTick will run on. Must not be null. - virtual TaskQueueBase* OnTickTaskQueue() = 0; - }; - virtual ~Metronome() = default; - // Adds a tick listener to the metronome. Once this method has returned - // OnTick will be invoked on each metronome tick. A listener may - // only be added to the metronome once. - virtual void AddListener(TickListener* listener) = 0; - - // Removes the tick listener from the metronome. Once this method has returned - // OnTick will never be called again. This method must not be called from - // within OnTick. - virtual void RemoveListener(TickListener* listener) = 0; + // Requests a call to `callback` on the next tick. Scheduled callbacks are + // executed on the same sequence as they were requested on. There are no + // features for cancellation. When that's needed, use e.g. ScopedTaskSafety + // from the client. + virtual void RequestCallOnNextTick(absl::AnyInvocable callback) {} // Returns the current tick period of the metronome. virtual TimeDelta TickPeriod() const = 0; diff --git a/api/metronome/test/BUILD.gn b/api/metronome/test/BUILD.gn index d25d5a848a..f415d98a0b 100644 --- a/api/metronome/test/BUILD.gn +++ b/api/metronome/test/BUILD.gn @@ -23,7 +23,7 @@ rtc_library("fake_metronome") { "../../../rtc_base:rtc_task_queue", "../../../rtc_base/synchronization:mutex", "../../../rtc_base/task_utils:repeating_task", - "../../../rtc_base/task_utils:to_queued_task", + "../../../test:test_support", "../../task_queue", "../../units:time_delta", ] diff --git a/api/metronome/test/fake_metronome.cc b/api/metronome/test/fake_metronome.cc index 83b5ea7604..025f7ce5a6 100644 --- a/api/metronome/test/fake_metronome.cc +++ b/api/metronome/test/fake_metronome.cc @@ -10,25 +10,25 @@ #include "api/metronome/test/fake_metronome.h" +#include +#include + #include "api/priority.h" #include "api/sequence_checker.h" +#include "api/task_queue/task_queue_base.h" #include "api/task_queue/task_queue_factory.h" #include "api/units/time_delta.h" #include "rtc_base/event.h" #include "rtc_base/task_utils/repeating_task.h" -#include "rtc_base/task_utils/to_queued_task.h" namespace webrtc::test { ForcedTickMetronome::ForcedTickMetronome(TimeDelta tick_period) : tick_period_(tick_period) {} -void ForcedTickMetronome::AddListener(TickListener* listener) { - listeners_.insert(listener); -} - -void ForcedTickMetronome::RemoveListener(TickListener* listener) { - listeners_.erase(listener); +void ForcedTickMetronome::RequestCallOnNextTick( + absl::AnyInvocable callback) { + callbacks_.push_back(std::move(callback)); } TimeDelta ForcedTickMetronome::TickPeriod() const { @@ -36,56 +36,35 @@ TimeDelta ForcedTickMetronome::TickPeriod() const { } size_t ForcedTickMetronome::NumListeners() { - return listeners_.size(); + return callbacks_.size(); } void ForcedTickMetronome::Tick() { - for (auto* listener : listeners_) { - listener->OnTickTaskQueue()->PostTask( - ToQueuedTask([listener] { listener->OnTick(); })); - } + std::vector> callbacks; + callbacks_.swap(callbacks); + for (auto& callback : callbacks) + std::move(callback)(); } -FakeMetronome::FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period) - : tick_period_(tick_period), - queue_(factory->CreateTaskQueue("MetronomeQueue", - TaskQueueFactory::Priority::HIGH)) {} - -FakeMetronome::~FakeMetronome() { - RTC_DCHECK(listeners_.empty()); -} +FakeMetronome::FakeMetronome(TimeDelta tick_period) + : tick_period_(tick_period) {} -void FakeMetronome::AddListener(TickListener* listener) { - MutexLock lock(&mutex_); - listeners_.insert(listener); - if (!started_) { - tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] { - MutexLock lock(&mutex_); - // Stop if empty. - if (listeners_.empty()) - return TimeDelta::PlusInfinity(); - for (auto* listener : listeners_) { - listener->OnTickTaskQueue()->PostTask( - ToQueuedTask([listener] { listener->OnTick(); })); - } - return tick_period_; - }); - started_ = true; +void FakeMetronome::RequestCallOnNextTick( + absl::AnyInvocable callback) { + TaskQueueBase* current = TaskQueueBase::Current(); + callbacks_.push_back(std::move(callback)); + if (callbacks_.size() == 1) { + current->PostDelayedTask( + [this] { + std::vector> callbacks; + callbacks_.swap(callbacks); + for (auto& callback : callbacks) + std::move(callback)(); + }, + tick_period_); } } -void FakeMetronome::RemoveListener(TickListener* listener) { - MutexLock lock(&mutex_); - listeners_.erase(listener); -} - -void FakeMetronome::Stop() { - MutexLock lock(&mutex_); - RTC_DCHECK(listeners_.empty()); - if (started_) - queue_.PostTask([this] { tick_task_.Stop(); }); -} - TimeDelta FakeMetronome::TickPeriod() const { return tick_period_; } diff --git a/api/metronome/test/fake_metronome.h b/api/metronome/test/fake_metronome.h index 28a79e06ff..73c938e9cd 100644 --- a/api/metronome/test/fake_metronome.h +++ b/api/metronome/test/fake_metronome.h @@ -13,6 +13,7 @@ #include #include +#include #include "api/metronome/metronome.h" #include "api/task_queue/task_queue_base.h" @@ -36,13 +37,12 @@ class ForcedTickMetronome : public Metronome { size_t NumListeners(); // Metronome implementation. - void AddListener(TickListener* listener) override; - void RemoveListener(TickListener* listener) override; + void RequestCallOnNextTick(absl::AnyInvocable callback) override; TimeDelta TickPeriod() const override; private: const TimeDelta tick_period_; - std::set listeners_; + std::vector> callbacks_; }; // FakeMetronome is a metronome that ticks based on a repeating task at the @@ -53,23 +53,15 @@ class ForcedTickMetronome : public Metronome { // on the proper task queue. class FakeMetronome : public Metronome { public: - FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period); - ~FakeMetronome() override; + explicit FakeMetronome(TimeDelta tick_period); // Metronome implementation. - void AddListener(TickListener* listener) override; - void RemoveListener(TickListener* listener) override; + void RequestCallOnNextTick(absl::AnyInvocable callback) override; TimeDelta TickPeriod() const override; - void Stop(); - private: const TimeDelta tick_period_; - RepeatingTaskHandle tick_task_; - bool started_ RTC_GUARDED_BY(mutex_) = false; - std::set listeners_ RTC_GUARDED_BY(mutex_); - Mutex mutex_; - rtc::TaskQueue queue_; + std::vector> callbacks_; }; } // namespace webrtc::test diff --git a/api/neteq/OWNERS b/api/neteq/OWNERS index da887989eb..11257808f2 100644 --- a/api/neteq/OWNERS +++ b/api/neteq/OWNERS @@ -1,2 +1,3 @@ ivoc@webrtc.org henrik.lundin@webrtc.org +jakobi@webrtc.org diff --git a/api/neteq/neteq.h b/api/neteq/neteq.h index aed18faefc..5300c5601e 100644 --- a/api/neteq/neteq.h +++ b/api/neteq/neteq.h @@ -67,11 +67,13 @@ struct NetEqLifetimeStatistics { uint64_t jitter_buffer_delay_ms = 0; uint64_t jitter_buffer_emitted_count = 0; uint64_t jitter_buffer_target_delay_ms = 0; + uint64_t jitter_buffer_minimum_delay_ms = 0; uint64_t inserted_samples_for_deceleration = 0; uint64_t removed_samples_for_acceleration = 0; uint64_t silent_concealed_samples = 0; uint64_t fec_packets_received = 0; uint64_t fec_packets_discarded = 0; + uint64_t packets_discarded = 0; // Below stats are not part of the spec. uint64_t delayed_packet_outage_samples = 0; // This is sum of relative packet arrival delays of received packets so far. @@ -102,8 +104,6 @@ struct NetEqOperationsAndState { uint64_t accelerate_samples = 0; // Count of the number of buffer flushes. uint64_t packet_buffer_flushes = 0; - // The number of primary packets that were discarded. - uint64_t discarded_primary_packets = 0; // The statistics below are not cumulative. // The waiting time of the last decoded packet. uint64_t last_waiting_time_ms = 0; @@ -128,7 +128,7 @@ class NetEq { std::string ToString() const; - int sample_rate_hz = 16000; // Initial value. Will change with input data. + int sample_rate_hz = 48000; // Initial value. Will change with input data. bool enable_post_decode_vad = false; size_t max_packets_in_buffer = 200; int max_delay_ms = 0; @@ -313,12 +313,6 @@ class NetEq { virtual std::vector GetNackList( int64_t round_trip_time_ms) const = 0; - // Returns a vector containing the timestamps of the packets that were decoded - // in the last GetAudio call. If no packets were decoded in the last call, the - // vector is empty. - // Mainly intended for testing. - virtual std::vector LastDecodedTimestamps() const = 0; - // Returns the length of the audio yet to play in the sync buffer. // Mainly intended for testing. virtual int SyncBufferSizeMs() const = 0; diff --git a/api/neteq/neteq_controller.h b/api/neteq/neteq_controller.h index 2f203f4344..f0101d3d1a 100644 --- a/api/neteq/neteq_controller.h +++ b/api/neteq/neteq_controller.h @@ -163,6 +163,12 @@ class NetEqController { // Returns the target buffer level in ms. virtual int TargetLevelMs() const = 0; + // Returns the target buffer level in ms as it would be if no minimum or + // maximum delay was set. + // TODO(bugs.webrtc.org/14270): Make pure virtual once all implementations are + // updated. + virtual int UnlimitedTargetLevelMs() const { return 0; } + // Notify the NetEqController that a packet has arrived. Returns the relative // arrival delay, if it can be computed. virtual absl::optional PacketArrived(int fs_hz, @@ -170,7 +176,7 @@ class NetEqController { const PacketArrivedInfo& info) = 0; // Notify the NetEqController that we are currently in muted state. - // TODO(ivoc): Make pure virtual when downstream is updated. + // TODO(bugs.webrtc.org/14270): Make pure virtual when downstream is updated. virtual void NotifyMutedState() {} // Returns true if a peak was found. diff --git a/api/numerics/samples_stats_counter.cc b/api/numerics/samples_stats_counter.cc index 36871a6713..4eb0cde299 100644 --- a/api/numerics/samples_stats_counter.cc +++ b/api/numerics/samples_stats_counter.cc @@ -19,6 +19,10 @@ namespace webrtc { SamplesStatsCounter::SamplesStatsCounter() = default; +SamplesStatsCounter::SamplesStatsCounter(size_t expected_samples_count) { + samples_.reserve(expected_samples_count); +} + SamplesStatsCounter::~SamplesStatsCounter() = default; SamplesStatsCounter::SamplesStatsCounter(const SamplesStatsCounter&) = default; SamplesStatsCounter& SamplesStatsCounter::operator=( diff --git a/api/numerics/samples_stats_counter.h b/api/numerics/samples_stats_counter.h index 16d5d2a891..9d72296317 100644 --- a/api/numerics/samples_stats_counter.h +++ b/api/numerics/samples_stats_counter.h @@ -11,6 +11,8 @@ #ifndef API_NUMERICS_SAMPLES_STATS_COUNTER_H_ #define API_NUMERICS_SAMPLES_STATS_COUNTER_H_ +#include +#include #include #include "api/array_view.h" @@ -27,9 +29,12 @@ class SamplesStatsCounter { struct StatsSample { double value; Timestamp time; + // Sample's specific metadata. + std::map metadata; }; SamplesStatsCounter(); + explicit SamplesStatsCounter(size_t expected_samples_count); ~SamplesStatsCounter(); SamplesStatsCounter(const SamplesStatsCounter&); SamplesStatsCounter& operator=(const SamplesStatsCounter&); diff --git a/api/peer_connection_interface.cc b/api/peer_connection_interface.cc index 9f159ea731..d01d58d32b 100644 --- a/api/peer_connection_interface.cc +++ b/api/peer_connection_interface.cc @@ -41,11 +41,6 @@ PeerConnectionInterface::RTCConfiguration::RTCConfiguration( PeerConnectionInterface::RTCConfiguration::~RTCConfiguration() = default; -RTCError PeerConnectionInterface::SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& config) { - return RTCError(); -} - PeerConnectionDependencies::PeerConnectionDependencies( PeerConnectionObserver* observer_in) : observer(observer_in) {} diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h index c326799edb..1097a1639c 100644 --- a/api/peer_connection_interface.h +++ b/api/peer_connection_interface.h @@ -94,6 +94,7 @@ #include "api/field_trials_view.h" #include "api/ice_transport_interface.h" #include "api/jsep.h" +#include "api/legacy_stats_types.h" #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/metronome/metronome.h" @@ -112,7 +113,6 @@ #include "api/set_local_description_observer_interface.h" #include "api/set_remote_description_observer_interface.h" #include "api/stats/rtc_stats_collector_callback.h" -#include "api/stats_types.h" #include "api/task_queue/task_queue_factory.h" #include "api/transport/bitrate_settings.h" #include "api/transport/enums.h" @@ -125,9 +125,8 @@ #include "media/base/media_engine.h" // TODO(bugs.webrtc.org/7447): We plan to provide a way to let applications // inject a PacketSocketFactory and/or NetworkManager, and not expose -// PortAllocator in the PeerConnection api. This will let us remove nogncheck. -#include "p2p/base/port.h" // nogncheck -#include "p2p/base/port_allocator.h" // nogncheck +// PortAllocator in the PeerConnection api. +#include "p2p/base/port_allocator.h" #include "rtc_base/network.h" #include "rtc_base/network_constants.h" #include "rtc_base/network_monitor_factory.h" @@ -427,11 +426,6 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // default will be used. ////////////////////////////////////////////////////////////////////////// - // If set to true, don't gather IPv6 ICE candidates. - // 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. // Only intended to be used on specific devices. Certain phones disable IPv6 // when the screen is turned off and it would be better to just disable the @@ -491,10 +485,6 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // The minimum delay in milliseconds for the audio jitter buffer. int audio_jitter_buffer_min_delay_ms = 0; - // Whether the audio jitter buffer adapts the delay to retransmitted - // packets. - bool audio_jitter_buffer_enable_rtx_handling = false; - // Timeout in milliseconds before an ICE candidate pair is considered to be // "not receiving", after which a lower priority candidate pair may be // selected. @@ -698,6 +688,9 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { PortAllocatorConfig port_allocator_config; + // The burst interval of the pacer, see TaskQueuePacedSender constructor. + absl::optional pacer_burst_interval; + // // Don't forget to update operator== if adding something. // @@ -809,6 +802,16 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { rtc::scoped_refptr track, const std::vector& stream_ids) = 0; + // Add a new MediaStreamTrack as above, but with an additional parameter, + // `init_send_encodings` : initial RtpEncodingParameters for RtpSender, + // similar to init_send_encodings in RtpTransceiverInit. + // Note that a new transceiver will always be created. + // + virtual RTCErrorOr> AddTrack( + rtc::scoped_refptr track, + const std::vector& stream_ids, + const std::vector& init_send_encodings) = 0; + // Removes the connection between a MediaStreamTrack and the PeerConnection. // Stops sending on the RtpSender and marks the // corresponding RtpTransceiver direction as no longer sending. @@ -955,8 +958,6 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { rtc::scoped_refptr selector, rtc::scoped_refptr callback) = 0; // Clear cached stats in the RTCStatsCollector. - // Exposed for testing while waiting for automatic cache clear to work. - // https://bugs.webrtc.org/8693 virtual void ClearStatsCache() {} // Create a data channel with the provided config, or default config if none @@ -1111,11 +1112,8 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface { // - SYNTAX_ERROR if parsing an ICE server URL failed. // - INVALID_PARAMETER if a TURN server is missing `username` or `password`. // - INTERNAL_ERROR if an unexpected error occurred. - // - // TODO(nisse): Make this pure virtual once all Chrome subclasses of - // PeerConnectionInterface implement it. virtual RTCError SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& config); + const PeerConnectionInterface::RTCConfiguration& config) = 0; // Provides a remote candidate to the ICE Agent. // A copy of the `candidate` will be created and added to the remote @@ -1384,10 +1382,9 @@ struct RTC_EXPORT PeerConnectionDependencies final { PeerConnectionObserver* observer = nullptr; // Optional dependencies // TODO(bugs.webrtc.org/7447): remove port allocator once downstream is - // updated. For now, you can only set one of allocator and - // packet_socket_factory, not both. + // updated. The recommended way to inject networking components is to pass a + // PacketSocketFactory when creating the PeerConnectionFactory. std::unique_ptr allocator; - std::unique_ptr packet_socket_factory; // Factory for creating resolvers that look up hostnames in DNS std::unique_ptr async_dns_resolver_factory; @@ -1427,6 +1424,9 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final { rtc::Thread* worker_thread = nullptr; rtc::Thread* signaling_thread = nullptr; rtc::SocketFactory* socket_factory = nullptr; + // The `packet_socket_factory` will only be used if CreatePeerConnection is + // called without a `port_allocator`. + std::unique_ptr packet_socket_factory; std::unique_ptr task_queue_factory; std::unique_ptr media_engine; std::unique_ptr call_factory; @@ -1435,9 +1435,12 @@ struct RTC_EXPORT PeerConnectionFactoryDependencies final { std::unique_ptr network_state_predictor_factory; std::unique_ptr network_controller_factory; - // This will only be used if CreatePeerConnection is called without a - // `port_allocator`, causing the default allocator and network manager to be - // used. + // The `network_manager` will only be used if CreatePeerConnection is called + // without a `port_allocator`, causing the default allocator and network + // manager to be used. + std::unique_ptr network_manager; + // The `network_monitor_factory` will only be used if CreatePeerConnection is + // called without a `port_allocator`, and the above `network_manager' is null. std::unique_ptr network_monitor_factory; std::unique_ptr neteq_factory; std::unique_ptr sctp_factory; diff --git a/api/rtc_event_log/rtc_event.h b/api/rtc_event_log/rtc_event.h index 8697a25a74..aa74944fe5 100644 --- a/api/rtc_event_log/rtc_event.h +++ b/api/rtc_event_log/rtc_event.h @@ -54,8 +54,10 @@ class RtcEvent { GenericPacketReceived, GenericAckReceived, FrameDecoded, + NetEqSetMinimumDelay, BeginV3Log = 0x2501580, - EndV3Log = 0x2501581 + EndV3Log = 0x2501581, + FakeEvent, // For unit testing. }; RtcEvent(); diff --git a/api/rtc_event_log/rtc_event_log_factory.cc b/api/rtc_event_log/rtc_event_log_factory.cc index fdf267b7ba..a3cb68cf54 100644 --- a/api/rtc_event_log/rtc_event_log_factory.cc +++ b/api/rtc_event_log/rtc_event_log_factory.cc @@ -27,16 +27,22 @@ RtcEventLogFactory::RtcEventLogFactory(TaskQueueFactory* task_queue_factory) RTC_DCHECK(task_queue_factory_); } -std::unique_ptr RtcEventLogFactory::CreateRtcEventLog( - RtcEventLog::EncodingType encoding_type) { +std::unique_ptr RtcEventLogFactory::Create( + RtcEventLog::EncodingType encoding_type) const { #ifdef WEBRTC_ENABLE_RTC_EVENT_LOG if (field_trial::IsEnabled("WebRTC-RtcEventLogKillSwitch")) { return std::make_unique(); } - return std::make_unique(encoding_type, task_queue_factory_); + return std::make_unique( + RtcEventLogImpl::CreateEncoder(encoding_type), task_queue_factory_); #else return std::make_unique(); #endif } +std::unique_ptr RtcEventLogFactory::CreateRtcEventLog( + RtcEventLog::EncodingType encoding_type) { + return Create(encoding_type); +} + } // namespace webrtc diff --git a/api/rtc_event_log/rtc_event_log_factory.h b/api/rtc_event_log/rtc_event_log_factory.h index 06cc074d20..fd1db3c728 100644 --- a/api/rtc_event_log/rtc_event_log_factory.h +++ b/api/rtc_event_log/rtc_event_log_factory.h @@ -25,6 +25,8 @@ class RTC_EXPORT RtcEventLogFactory : public RtcEventLogFactoryInterface { explicit RtcEventLogFactory(TaskQueueFactory* task_queue_factory); ~RtcEventLogFactory() override {} + std::unique_ptr Create( + RtcEventLog::EncodingType encoding_type) const override; std::unique_ptr CreateRtcEventLog( RtcEventLog::EncodingType encoding_type) override; diff --git a/api/rtc_event_log/rtc_event_log_factory_interface.h b/api/rtc_event_log/rtc_event_log_factory_interface.h index acc5bcb038..a6f4dee92f 100644 --- a/api/rtc_event_log/rtc_event_log_factory_interface.h +++ b/api/rtc_event_log/rtc_event_log_factory_interface.h @@ -24,7 +24,9 @@ class RtcEventLogFactoryInterface { public: virtual ~RtcEventLogFactoryInterface() = default; - virtual std::unique_ptr CreateRtcEventLog( + virtual std::unique_ptr Create( + RtcEventLog::EncodingType encoding_type) const = 0; + [[deprecated]] virtual std::unique_ptr CreateRtcEventLog( RtcEventLog::EncodingType encoding_type) = 0; }; diff --git a/api/rtc_event_log_output.h b/api/rtc_event_log_output.h index cd16b27501..f1f84a5f3a 100644 --- a/api/rtc_event_log_output.h +++ b/api/rtc_event_log_output.h @@ -13,6 +13,8 @@ #include +#include "absl/strings/string_view.h" + namespace webrtc { // NOTE: This class is still under development and may change without notice. @@ -31,7 +33,7 @@ class RtcEventLogOutput { // about how much data was written, if any. The output sink becomes inactive // after the first time `false` is returned. Write() may not be called on // an inactive output sink. - virtual bool Write(const std::string& output) = 0; + virtual bool Write(absl::string_view output) = 0; // Indicates that buffers should be written to disk if applicable. virtual void Flush() {} diff --git a/api/rtc_event_log_output_file.cc b/api/rtc_event_log_output_file.cc index 2e31c2df66..e1d4c7c711 100644 --- a/api/rtc_event_log_output_file.cc +++ b/api/rtc_event_log_output_file.cc @@ -54,15 +54,15 @@ bool RtcEventLogOutputFile::IsActive() const { return IsActiveInternal(); } -bool RtcEventLogOutputFile::Write(const std::string& output) { +bool RtcEventLogOutputFile::Write(absl::string_view output) { RTC_DCHECK(IsActiveInternal()); // No single write may be so big, that it would risk overflowing the // calculation of (written_bytes_ + output.length()). - RTC_DCHECK_LT(output.length(), kMaxReasonableFileSize); + RTC_DCHECK_LT(output.size(), kMaxReasonableFileSize); if (max_size_bytes_ == RtcEventLog::kUnlimitedOutput || - written_bytes_ + output.length() <= max_size_bytes_) { - if (file_.Write(output.c_str(), output.size())) { + written_bytes_ + output.size() <= max_size_bytes_) { + if (file_.Write(output.data(), output.size())) { written_bytes_ += output.size(); return true; } else { diff --git a/api/rtc_event_log_output_file.h b/api/rtc_event_log_output_file.h index d2901be1d0..c9ae0a8ede 100644 --- a/api/rtc_event_log_output_file.h +++ b/api/rtc_event_log_output_file.h @@ -37,7 +37,7 @@ class RtcEventLogOutputFile final : public RtcEventLogOutput { bool IsActive() const override; - bool Write(const std::string& output) override; + bool Write(absl::string_view output) override; private: RtcEventLogOutputFile(FileWrapper file, size_t max_size_bytes); diff --git a/api/rtc_event_log_output_file_unittest.cc b/api/rtc_event_log_output_file_unittest.cc index 4274215491..0aff57fbbc 100644 --- a/api/rtc_event_log_output_file_unittest.cc +++ b/api/rtc_event_log_output_file_unittest.cc @@ -72,7 +72,7 @@ TEST_F(RtcEventLogOutputFileTest, UnlimitedOutputFile) { EXPECT_EQ(GetOutputFileContents(), output_str); } -// Do not allow writing more bytes to the file than +// Do not allow writing more bytes to the file than max file size. TEST_F(RtcEventLogOutputFileTest, LimitedOutputFileCappedToCapacity) { // Fit two bytes, then the third should be rejected. auto output_file = diff --git a/api/rtp_headers.h b/api/rtp_headers.h index cf3d909499..743fd6d1b6 100644 --- a/api/rtp_headers.h +++ b/api/rtp_headers.h @@ -103,15 +103,6 @@ struct RTPHeaderExtension { (1 << kAbsSendTimeFraction)); } - TimeDelta GetAbsoluteSendTimeDelta(uint32_t previous_sendtime) const { - RTC_DCHECK(hasAbsoluteSendTime); - RTC_DCHECK(absoluteSendTime < (1ul << 24)); - RTC_DCHECK(previous_sendtime < (1ul << 24)); - int32_t delta = - static_cast((absoluteSendTime - previous_sendtime) << 8) >> 8; - return TimeDelta::Micros((delta * 1000000ll) / (1 << kAbsSendTimeFraction)); - } - bool hasTransmissionTimeOffset; int32_t transmissionTimeOffset; bool hasAbsoluteSendTime; @@ -157,7 +148,7 @@ struct RTPHeaderExtension { enum { kRtpCsrcSize = 15 }; // RFC 3550 page 13 -struct RTPHeader { +struct RTC_EXPORT RTPHeader { RTPHeader(); RTPHeader(const RTPHeader& other); RTPHeader& operator=(const RTPHeader& other); diff --git a/api/rtp_packet_info.cc b/api/rtp_packet_info.cc index db818f7657..cba274ec38 100644 --- a/api/rtp_packet_info.cc +++ b/api/rtp_packet_info.cc @@ -18,18 +18,13 @@ namespace webrtc { RtpPacketInfo::RtpPacketInfo() : ssrc_(0), rtp_timestamp_(0), receive_time_(Timestamp::MinusInfinity()) {} -RtpPacketInfo::RtpPacketInfo( - uint32_t ssrc, - std::vector csrcs, - uint32_t rtp_timestamp, - absl::optional audio_level, - absl::optional absolute_capture_time, - Timestamp receive_time) +RtpPacketInfo::RtpPacketInfo(uint32_t ssrc, + std::vector csrcs, + uint32_t rtp_timestamp, + Timestamp receive_time) : ssrc_(ssrc), csrcs_(std::move(csrcs)), rtp_timestamp_(rtp_timestamp), - audio_level_(audio_level), - absolute_capture_time_(absolute_capture_time), receive_time_(receive_time) {} RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, @@ -49,31 +44,13 @@ RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, absolute_capture_time_ = extension.absolute_capture_time; } -RtpPacketInfo::RtpPacketInfo( - uint32_t ssrc, - std::vector csrcs, - uint32_t rtp_timestamp, - absl::optional audio_level, - absl::optional absolute_capture_time, - int64_t receive_time_ms) - : RtpPacketInfo(ssrc, - csrcs, - rtp_timestamp, - audio_level, - absolute_capture_time, - Timestamp::Millis(receive_time_ms)) {} -RtpPacketInfo::RtpPacketInfo(const RTPHeader& rtp_header, - int64_t receive_time_ms) - : RtpPacketInfo(rtp_header, Timestamp::Millis(receive_time_ms)) {} - bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs) { return (lhs.ssrc() == rhs.ssrc()) && (lhs.csrcs() == rhs.csrcs()) && (lhs.rtp_timestamp() == rhs.rtp_timestamp()) && + (lhs.receive_time() == rhs.receive_time()) && (lhs.audio_level() == rhs.audio_level()) && (lhs.absolute_capture_time() == rhs.absolute_capture_time()) && - (lhs.receive_time() == rhs.receive_time() && - (lhs.local_capture_clock_offset() == - rhs.local_capture_clock_offset())); + (lhs.local_capture_clock_offset() == rhs.local_capture_clock_offset()); } } // namespace webrtc diff --git a/api/rtp_packet_info.h b/api/rtp_packet_info.h index bc9839f479..8df12a36cf 100644 --- a/api/rtp_packet_info.h +++ b/api/rtp_packet_info.h @@ -17,6 +17,7 @@ #include "absl/types/optional.h" #include "api/rtp_headers.h" +#include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "rtc_base/system/rtc_export.h" @@ -34,22 +35,10 @@ class RTC_EXPORT RtpPacketInfo { RtpPacketInfo(uint32_t ssrc, std::vector csrcs, uint32_t rtp_timestamp, - absl::optional audio_level, - absl::optional absolute_capture_time, Timestamp receive_time); RtpPacketInfo(const RTPHeader& rtp_header, Timestamp receive_time); - // TODO(bugs.webrtc.org/12722): Deprecated, remove once downstream projects - // are updated. - RtpPacketInfo(uint32_t ssrc, - std::vector csrcs, - uint32_t rtp_timestamp, - absl::optional audio_level, - absl::optional absolute_capture_time, - int64_t receive_time_ms); - RtpPacketInfo(const RTPHeader& rtp_header, int64_t receive_time_ms); - RtpPacketInfo(const RtpPacketInfo& other) = default; RtpPacketInfo(RtpPacketInfo&& other) = default; RtpPacketInfo& operator=(const RtpPacketInfo& other) = default; @@ -64,31 +53,33 @@ class RTC_EXPORT RtpPacketInfo { uint32_t rtp_timestamp() const { return rtp_timestamp_; } void set_rtp_timestamp(uint32_t value) { rtp_timestamp_ = value; } + Timestamp receive_time() const { return receive_time_; } + void set_receive_time(Timestamp value) { receive_time_ = value; } + absl::optional audio_level() const { return audio_level_; } - void set_audio_level(absl::optional value) { audio_level_ = value; } + RtpPacketInfo& set_audio_level(absl::optional value) { + audio_level_ = value; + return *this; + } const absl::optional& absolute_capture_time() const { return absolute_capture_time_; } - void set_absolute_capture_time( + RtpPacketInfo& set_absolute_capture_time( const absl::optional& value) { absolute_capture_time_ = value; + return *this; } - const absl::optional& local_capture_clock_offset() const { + const absl::optional& local_capture_clock_offset() const { return local_capture_clock_offset_; } - - void set_local_capture_clock_offset(const absl::optional& value) { + RtpPacketInfo& set_local_capture_clock_offset( + absl::optional value) { local_capture_clock_offset_ = value; + return *this; } - Timestamp receive_time() const { return receive_time_; } - void set_receive_time(Timestamp value) { receive_time_ = value; } - // TODO(bugs.webrtc.org/12722): Deprecated, remove once downstream projects - // are updated. - int64_t receive_time_ms() const { return receive_time_.ms(); } - private: // Fields from the RTP header: // https://tools.ietf.org/html/rfc3550#section-5.1 @@ -96,25 +87,23 @@ class RTC_EXPORT RtpPacketInfo { std::vector csrcs_; uint32_t rtp_timestamp_; + // Local `webrtc::Clock`-based timestamp of when the packet was received. + Timestamp receive_time_; + // Fields from the Audio Level header extension: // https://tools.ietf.org/html/rfc6464#section-3 absl::optional audio_level_; // Fields from the Absolute Capture Time header extension: // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time - // To not be confused with `local_capture_clock_offset_`, the - // `estimated_capture_clock_offset` in `absolute_capture_time_` should - // represent the clock offset between a remote sender and the capturer, and - // thus equals to the corresponding values in the received RTP packets, - // subjected to possible interpolations. absl::optional absolute_capture_time_; - // Clock offset against capturer's clock. Should be derived from the estimated - // capture clock offset defined in the Absolute Capture Time header extension. - absl::optional local_capture_clock_offset_; - - // Local `webrtc::Clock`-based timestamp of when the packet was received. - Timestamp receive_time_; + // Clock offset between the local clock and the capturer's clock. + // Do not confuse with `AbsoluteCaptureTime::estimated_capture_clock_offset` + // which instead represents the clock offset between a remote sender and the + // capturer. The following holds: + // Capture's NTP Clock = Local NTP Clock + Local-Capture Clock Offset + absl::optional local_capture_clock_offset_; }; bool operator==(const RtpPacketInfo& lhs, const RtpPacketInfo& rhs); diff --git a/api/rtp_packet_info_unittest.cc b/api/rtp_packet_info_unittest.cc index 601d34f49e..d35edf75db 100644 --- a/api/rtp_packet_info_unittest.cc +++ b/api/rtp_packet_info_unittest.cc @@ -9,13 +9,14 @@ */ #include "api/rtp_packet_infos.h" +#include "api/units/time_delta.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { TEST(RtpPacketInfoTest, Ssrc) { - const uint32_t value = 4038189233; + constexpr uint32_t kValue = 4038189233; RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -23,8 +24,8 @@ TEST(RtpPacketInfoTest, Ssrc) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_ssrc(value); - EXPECT_EQ(rhs.ssrc(), value); + rhs.set_ssrc(kValue); + EXPECT_EQ(rhs.ssrc(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -35,10 +36,11 @@ TEST(RtpPacketInfoTest, Ssrc) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.ssrc(), value); + EXPECT_NE(rhs.ssrc(), kValue); - rhs = RtpPacketInfo(value, {}, {}, {}, {}, Timestamp::Millis(0)); - EXPECT_EQ(rhs.ssrc(), value); + rhs = RtpPacketInfo(/*ssrc=*/kValue, /*csrcs=*/{}, /*rtp_timestamp=*/{}, + /*receive_time=*/Timestamp::Zero()); + EXPECT_EQ(rhs.ssrc(), kValue); } TEST(RtpPacketInfoTest, Csrcs) { @@ -64,12 +66,13 @@ TEST(RtpPacketInfoTest, Csrcs) { rhs = RtpPacketInfo(); EXPECT_NE(rhs.csrcs(), value); - rhs = RtpPacketInfo({}, value, {}, {}, {}, Timestamp::Millis(0)); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/value, /*rtp_timestamp=*/{}, + /*receive_time=*/Timestamp::Zero()); EXPECT_EQ(rhs.csrcs(), value); } TEST(RtpPacketInfoTest, RtpTimestamp) { - const uint32_t value = 4038189233; + constexpr uint32_t kValue = 4038189233; RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -77,8 +80,8 @@ TEST(RtpPacketInfoTest, RtpTimestamp) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_rtp_timestamp(value); - EXPECT_EQ(rhs.rtp_timestamp(), value); + rhs.set_rtp_timestamp(kValue); + EXPECT_EQ(rhs.rtp_timestamp(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -89,14 +92,15 @@ TEST(RtpPacketInfoTest, RtpTimestamp) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.rtp_timestamp(), value); + EXPECT_NE(rhs.rtp_timestamp(), kValue); - rhs = RtpPacketInfo({}, {}, value, {}, {}, Timestamp::Millis(0)); - EXPECT_EQ(rhs.rtp_timestamp(), value); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/kValue, + /*receive_time=*/Timestamp::Zero()); + EXPECT_EQ(rhs.rtp_timestamp(), kValue); } -TEST(RtpPacketInfoTest, AudioLevel) { - const absl::optional value = 31; +TEST(RtpPacketInfoTest, ReceiveTimeMs) { + constexpr Timestamp kValue = Timestamp::Micros(8868963877546349045LL); RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -104,8 +108,8 @@ TEST(RtpPacketInfoTest, AudioLevel) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_audio_level(value); - EXPECT_EQ(rhs.audio_level(), value); + rhs.set_receive_time(kValue); + EXPECT_EQ(rhs.receive_time(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -116,14 +120,15 @@ TEST(RtpPacketInfoTest, AudioLevel) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.audio_level(), value); + EXPECT_NE(rhs.receive_time(), kValue); - rhs = RtpPacketInfo({}, {}, {}, value, {}, Timestamp::Millis(0)); - EXPECT_EQ(rhs.audio_level(), value); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, + /*receive_time=*/kValue); + EXPECT_EQ(rhs.receive_time(), kValue); } -TEST(RtpPacketInfoTest, AbsoluteCaptureTime) { - const absl::optional value = AbsoluteCaptureTime{12, 34}; +TEST(RtpPacketInfoTest, AudioLevel) { + constexpr absl::optional kValue = 31; RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -131,8 +136,8 @@ TEST(RtpPacketInfoTest, AbsoluteCaptureTime) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_absolute_capture_time(value); - EXPECT_EQ(rhs.absolute_capture_time(), value); + rhs.set_audio_level(kValue); + EXPECT_EQ(rhs.audio_level(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -143,22 +148,26 @@ TEST(RtpPacketInfoTest, AbsoluteCaptureTime) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.absolute_capture_time(), value); + EXPECT_NE(rhs.audio_level(), kValue); - rhs = RtpPacketInfo({}, {}, {}, {}, value, Timestamp::Millis(0)); - EXPECT_EQ(rhs.absolute_capture_time(), value); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, + /*receive_time=*/Timestamp::Zero()); + rhs.set_audio_level(kValue); + EXPECT_EQ(rhs.audio_level(), kValue); } -TEST(RtpPacketInfoTest, LocalCaptureClockOffset) { +TEST(RtpPacketInfoTest, AbsoluteCaptureTime) { + constexpr absl::optional kValue = AbsoluteCaptureTime{ + .absolute_capture_timestamp = 12, .estimated_capture_clock_offset = 34}; + RtpPacketInfo lhs; RtpPacketInfo rhs; EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - const absl::optional value = 10; - rhs.set_local_capture_clock_offset(value); - EXPECT_EQ(rhs.local_capture_clock_offset(), value); + rhs.set_absolute_capture_time(kValue); + EXPECT_EQ(rhs.absolute_capture_time(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -168,18 +177,17 @@ TEST(RtpPacketInfoTest, LocalCaptureClockOffset) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - // Default local capture clock offset is null. rhs = RtpPacketInfo(); - EXPECT_EQ(rhs.local_capture_clock_offset(), absl::nullopt); + EXPECT_NE(rhs.absolute_capture_time(), kValue); - // Default local capture clock offset is null. - rhs = RtpPacketInfo({}, {}, {}, {}, AbsoluteCaptureTime{12, 34}, - Timestamp::Millis(0)); - EXPECT_EQ(rhs.local_capture_clock_offset(), absl::nullopt); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, + /*receive_time=*/Timestamp::Zero()); + rhs.set_absolute_capture_time(kValue); + EXPECT_EQ(rhs.absolute_capture_time(), kValue); } -TEST(RtpPacketInfoTest, ReceiveTimeMs) { - const Timestamp timestamp = Timestamp::Micros(8868963877546349045LL); +TEST(RtpPacketInfoTest, LocalCaptureClockOffset) { + constexpr TimeDelta kValue = TimeDelta::Micros(8868963877546349045LL); RtpPacketInfo lhs; RtpPacketInfo rhs; @@ -187,8 +195,8 @@ TEST(RtpPacketInfoTest, ReceiveTimeMs) { EXPECT_TRUE(lhs == rhs); EXPECT_FALSE(lhs != rhs); - rhs.set_receive_time(timestamp); - EXPECT_EQ(rhs.receive_time(), timestamp); + rhs.set_local_capture_clock_offset(kValue); + EXPECT_EQ(rhs.local_capture_clock_offset(), kValue); EXPECT_FALSE(lhs == rhs); EXPECT_TRUE(lhs != rhs); @@ -199,10 +207,12 @@ TEST(RtpPacketInfoTest, ReceiveTimeMs) { EXPECT_FALSE(lhs != rhs); rhs = RtpPacketInfo(); - EXPECT_NE(rhs.receive_time(), timestamp); + EXPECT_EQ(rhs.local_capture_clock_offset(), absl::nullopt); - rhs = RtpPacketInfo({}, {}, {}, {}, {}, timestamp); - EXPECT_EQ(rhs.receive_time(), timestamp); + rhs = RtpPacketInfo(/*ssrc=*/{}, /*csrcs=*/{}, /*rtp_timestamp=*/{}, + /*receive_time=*/Timestamp::Zero()); + rhs.set_local_capture_clock_offset(kValue); + EXPECT_EQ(rhs.local_capture_clock_offset(), kValue); } } // namespace webrtc diff --git a/api/rtp_packet_infos.h b/api/rtp_packet_infos.h index 031e33332e..7445729fbb 100644 --- a/api/rtp_packet_infos.h +++ b/api/rtp_packet_infos.h @@ -15,10 +15,10 @@ #include #include +#include "api/make_ref_counted.h" #include "api/ref_counted_base.h" #include "api/rtp_packet_info.h" #include "api/scoped_refptr.h" -#include "rtc_base/ref_counted_object.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { diff --git a/api/rtp_packet_infos_unittest.cc b/api/rtp_packet_infos_unittest.cc index e83358fc17..a90cfa03e2 100644 --- a/api/rtp_packet_infos_unittest.cc +++ b/api/rtp_packet_infos_unittest.cc @@ -27,12 +27,23 @@ RtpPacketInfos::vector_type ToVector(Iterator begin, Iterator end) { } // namespace TEST(RtpPacketInfosTest, BasicFunctionality) { - RtpPacketInfo p0(123, {1, 2}, 89, 5, AbsoluteCaptureTime{45, 78}, - Timestamp::Millis(7)); - RtpPacketInfo p1(456, {3, 4}, 89, 4, AbsoluteCaptureTime{13, 21}, - Timestamp::Millis(1)); - RtpPacketInfo p2(789, {5, 6}, 88, 1, AbsoluteCaptureTime{99, 78}, - Timestamp::Millis(7)); + RtpPacketInfo p0(/*ssrc=*/123, /*csrcs=*/{1, 2}, /*rtp_timestamp=*/89, + /*receive_time=*/Timestamp::Millis(7)); + p0.set_audio_level(5); + p0.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 45, .estimated_capture_clock_offset = 78}); + + RtpPacketInfo p1(/*ssrc=*/456, /*csrcs=*/{3, 4}, /*rtp_timestamp=*/89, + /*receive_time=*/Timestamp::Millis(1)); + p1.set_audio_level(4); + p1.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 13, .estimated_capture_clock_offset = 21}); + + RtpPacketInfo p2(/*ssrc=*/789, /*csrcs=*/{5, 6}, /*rtp_timestamp=*/88, + /*receive_time=*/Timestamp::Millis(7)); + p2.set_audio_level(1); + p2.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 99, .estimated_capture_clock_offset = 78}); RtpPacketInfos x({p0, p1, p2}); @@ -55,12 +66,23 @@ TEST(RtpPacketInfosTest, BasicFunctionality) { } TEST(RtpPacketInfosTest, CopyShareData) { - RtpPacketInfo p0(123, {1, 2}, 89, 5, AbsoluteCaptureTime{45, 78}, - Timestamp::Millis(7)); - RtpPacketInfo p1(456, {3, 4}, 89, 4, AbsoluteCaptureTime{13, 21}, - Timestamp::Millis(1)); - RtpPacketInfo p2(789, {5, 6}, 88, 1, AbsoluteCaptureTime{99, 78}, - Timestamp::Millis(7)); + RtpPacketInfo p0(/*ssrc=*/123, /*csrcs=*/{1, 2}, /*rtp_timestamp=*/89, + /*receive_time=*/Timestamp::Millis(7)); + p0.set_audio_level(5); + p0.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 45, .estimated_capture_clock_offset = 78}); + + RtpPacketInfo p1(/*ssrc=*/456, /*csrcs=*/{3, 4}, /*rtp_timestamp=*/89, + /*receive_time=*/Timestamp::Millis(1)); + p1.set_audio_level(4); + p1.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 13, .estimated_capture_clock_offset = 21}); + + RtpPacketInfo p2(/*ssrc=*/789, /*csrcs=*/{5, 6}, /*rtp_timestamp=*/88, + /*receive_time=*/Timestamp::Millis(7)); + p2.set_audio_level(1); + p2.set_absolute_capture_time(AbsoluteCaptureTime{ + .absolute_capture_timestamp = 99, .estimated_capture_clock_offset = 78}); RtpPacketInfos lhs({p0, p1, p2}); RtpPacketInfos rhs = lhs; diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h index 45cedfdd9c..0d3c9dfd22 100644 --- a/api/rtp_parameters.h +++ b/api/rtp_parameters.h @@ -17,11 +17,14 @@ #include #include +#include "absl/container/inlined_vector.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/media_types.h" #include "api/priority.h" #include "api/rtp_transceiver_direction.h" +#include "api/video/resolution.h" +#include "api/video_codecs/scalability_mode.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -186,6 +189,9 @@ struct RTC_EXPORT RtpCodecCapability { // TODO(deadbeef): Not implemented. bool svc_multi_stream_support = false; + // https://w3c.github.io/webrtc-svc/#dom-rtcrtpcodeccapability-scalabilitymodes + absl::InlinedVector scalability_modes; + bool operator==(const RtpCodecCapability& o) const { return name == o.name && kind == o.kind && clock_rate == o.clock_rate && preferred_payload_type == o.preferred_payload_type && @@ -194,7 +200,8 @@ struct RTC_EXPORT RtpCodecCapability { parameters == o.parameters && options == o.options && max_temporal_layer_extensions == o.max_temporal_layer_extensions && max_spatial_layer_extensions == o.max_spatial_layer_extensions && - svc_multi_stream_support == o.svc_multi_stream_support; + svc_multi_stream_support == o.svc_multi_stream_support && + scalability_modes == o.scalability_modes; } bool operator!=(const RtpCodecCapability& o) const { return !(*this == o); } }; @@ -496,6 +503,24 @@ struct RTC_EXPORT RtpEncodingParameters { // https://w3c.github.io/webrtc-svc/#rtcrtpencodingparameters absl::optional scalability_mode; + // Requested encode resolution. + // + // This field provides an alternative to `scale_resolution_down_by` + // that is not dependent on the video source. + // + // When setting requested_resolution it is not necessary to adapt the + // video source using OnOutputFormatRequest, since the VideoStreamEncoder + // will apply downscaling if necessary. requested_resolution will also be + // propagated to the video source, this allows downscaling earlier in the + // pipeline which can be beneficial if the source is consumed by multiple + // encoders, but is not strictly necessary. + // + // The `requested_resolution` is subject to resource adaptation. + // + // It is an error to set both `requested_resolution` and + // `scale_resolution_down_by`. + absl::optional requested_resolution; + // For an RtpSender, set to true to cause this encoding to be encoded and // sent, and false for it not to be encoded and sent. This allows control // across multiple encodings of a sender for turning simulcast layers on and @@ -521,7 +546,8 @@ struct RTC_EXPORT RtpEncodingParameters { num_temporal_layers == o.num_temporal_layers && scale_resolution_down_by == o.scale_resolution_down_by && active == o.active && rid == o.rid && - adaptive_ptime == o.adaptive_ptime; + adaptive_ptime == o.adaptive_ptime && + requested_resolution == o.requested_resolution; } bool operator!=(const RtpEncodingParameters& o) const { return !(*this == o); diff --git a/api/rtp_sender_interface.cc b/api/rtp_sender_interface.cc index 57a5a10fb5..f1ca5c2203 100644 --- a/api/rtp_sender_interface.cc +++ b/api/rtp_sender_interface.cc @@ -1,5 +1,5 @@ /* - * Copyright 2018 The WebRTC Project Authors. All rights reserved. + * 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 @@ -10,27 +10,13 @@ #include "api/rtp_sender_interface.h" -namespace webrtc { - -void RtpSenderInterface::SetFrameEncryptor( - rtc::scoped_refptr frame_encryptor) {} - -rtc::scoped_refptr -RtpSenderInterface::GetFrameEncryptor() const { - return nullptr; -} +#include "rtc_base/checks.h" -std::vector RtpSenderInterface::init_send_encodings() - const { - return {}; -} +namespace webrtc { -rtc::scoped_refptr RtpSenderInterface::dtls_transport() - const { - return nullptr; +void RtpSenderInterface::SetParametersAsync(const RtpParameters& parameters, + SetParametersCallback callback) { + RTC_DCHECK_NOTREACHED() << "Default implementation called"; } -void RtpSenderInterface::SetEncoderToPacketizerFrameTransformer( - rtc::scoped_refptr frame_transformer) {} - } // namespace webrtc diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index 9ffad68644..2786a2ac19 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h @@ -14,9 +14,11 @@ #ifndef API_RTP_SENDER_INTERFACE_H_ #define API_RTP_SENDER_INTERFACE_H_ +#include #include #include +#include "absl/functional/any_invocable.h" #include "api/crypto/frame_encryptor_interface.h" #include "api/dtls_transport_interface.h" #include "api/dtmf_sender_interface.h" @@ -26,11 +28,14 @@ #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/video_codecs/video_encoder_factory.h" #include "rtc_base/ref_count.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { +using SetParametersCallback = absl::AnyInvocable; + class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface { public: // Returns true if successful in setting the track. @@ -41,8 +46,7 @@ class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface { // The dtlsTransport attribute exposes the DTLS transport on which the // media is sent. It may be null. // https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-transport - // TODO(https://bugs.webrtc.org/907849) remove default implementation - virtual rtc::scoped_refptr dtls_transport() const; + virtual rtc::scoped_refptr dtls_transport() const = 0; // Returns primary SSRC used by this sender for sending media. // Returns 0 if not yet determined. @@ -65,19 +69,21 @@ class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface { // Sets the IDs of the media streams associated with this sender's track. // These are signalled in the SDP so that the remote side can associate // tracks. - virtual void SetStreams(const std::vector& stream_ids) {} + virtual void SetStreams(const std::vector& stream_ids) = 0; // Returns the list of encoding parameters that will be applied when the SDP // local description is set. These initial encoding parameters can be set by // PeerConnection::AddTransceiver, and later updated with Get/SetParameters. // TODO(orphis): Make it pure virtual once Chrome has updated - virtual std::vector init_send_encodings() const; + virtual std::vector init_send_encodings() const = 0; virtual RtpParameters GetParameters() const = 0; // Note that only a subset of the parameters can currently be changed. See // rtpparameters.h // The encodings are in increasing quality order for simulcast. virtual RTCError SetParameters(const RtpParameters& parameters) = 0; + virtual void SetParametersAsync(const RtpParameters& parameters, + SetParametersCallback callback); // Returns null for a video sender. virtual rtc::scoped_refptr GetDtmfSender() const = 0; @@ -87,14 +93,26 @@ class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface { // using the user provided encryption mechanism regardless of whether SRTP is // enabled or not. virtual void SetFrameEncryptor( - rtc::scoped_refptr frame_encryptor); + rtc::scoped_refptr frame_encryptor) = 0; // Returns a pointer to the frame encryptor set previously by the // user. This can be used to update the state of the object. - virtual rtc::scoped_refptr GetFrameEncryptor() const; + virtual rtc::scoped_refptr GetFrameEncryptor() + const = 0; virtual void SetEncoderToPacketizerFrameTransformer( - rtc::scoped_refptr frame_transformer); + rtc::scoped_refptr frame_transformer) = 0; + + // Sets a user defined encoder selector. + // Overrides selector that is (optionally) provided by VideoEncoderFactory. + virtual void SetEncoderSelector( + std::unique_ptr + encoder_selector) = 0; + + // TODO(crbug.com/1354101): make pure virtual again after Chrome roll. + virtual RTCError GenerateKeyFrame(const std::vector& rids) { + return RTCError::OK(); + } protected: ~RtpSenderInterface() override = default; diff --git a/api/sequence_checker.h b/api/sequence_checker.h index 5db7b9e4df..a79d04f61f 100644 --- a/api/sequence_checker.h +++ b/api/sequence_checker.h @@ -107,10 +107,15 @@ class RTC_LOCKABLE SequenceChecker #define RTC_RUN_ON(x) \ RTC_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x)) -#define RTC_DCHECK_RUN_ON(x) \ - webrtc::webrtc_sequence_checker_internal::SequenceCheckerScope \ - seq_check_scope(x); \ - RTC_DCHECK((x)->IsCurrent()) \ - << webrtc::webrtc_sequence_checker_internal::ExpectationToString(x) +// Checks current code is running on the desired sequence. +// +// First statement validates it is running on the sequence `x`. +// Second statement annotates for the thread safety analyzer the check was done. +// Such annotation has to be attached to a function, and that function has to be +// called. Thus current implementation creates a noop lambda and calls it. +#define RTC_DCHECK_RUN_ON(x) \ + RTC_DCHECK((x)->IsCurrent()) \ + << webrtc::webrtc_sequence_checker_internal::ExpectationToString(x); \ + []() RTC_ASSERT_EXCLUSIVE_LOCK(x) {}() #endif // API_SEQUENCE_CHECKER_H_ diff --git a/api/sequence_checker_unittest.cc b/api/sequence_checker_unittest.cc index 21a0894a8e..a929c59655 100644 --- a/api/sequence_checker_unittest.cc +++ b/api/sequence_checker_unittest.cc @@ -14,11 +14,15 @@ #include #include "api/function_view.h" +#include "api/units/time_delta.h" #include "rtc_base/event.h" #include "rtc_base/platform_thread.h" #include "rtc_base/task_queue_for_test.h" +#include "test/gmock.h" #include "test/gtest.h" +using testing::HasSubstr; + namespace webrtc { namespace { @@ -47,7 +51,7 @@ void RunOnDifferentThread(rtc::FunctionView run) { thread_has_run_event.Set(); }, "thread"); - EXPECT_TRUE(thread_has_run_event.Wait(1000)); + EXPECT_TRUE(thread_has_run_event.Wait(TimeDelta::Seconds(1))); } } // namespace @@ -76,20 +80,16 @@ TEST(SequenceCheckerTest, DetachFromThreadAndUseOnTaskQueue) { SequenceChecker sequence_checker; sequence_checker.Detach(); TaskQueueForTest queue; - queue.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }, - RTC_FROM_HERE); + queue.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); } TEST(SequenceCheckerTest, DetachFromTaskQueueAndUseOnThread) { TaskQueueForTest queue; - queue.SendTask( - [] { - SequenceChecker sequence_checker; - sequence_checker.Detach(); - RunOnDifferentThread( - [&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); - }, - RTC_FROM_HERE); + queue.SendTask([] { + SequenceChecker sequence_checker; + sequence_checker.Detach(); + RunOnDifferentThread([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); + }); } TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) { @@ -102,8 +102,7 @@ TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentTaskQueueInDebug) { SequenceChecker sequence_checker; TaskQueueForTest queue; queue.SendTask( - [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }, - RTC_FROM_HERE); + [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }); } TEST(SequenceCheckerTest, DetachFromTaskQueueInDebug) { @@ -111,15 +110,43 @@ TEST(SequenceCheckerTest, DetachFromTaskQueueInDebug) { sequence_checker.Detach(); TaskQueueForTest queue1; - queue1.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }, - RTC_FROM_HERE); + queue1.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); }); // IsCurrent should return false in debug builds after moving to // another task queue. TaskQueueForTest queue2; queue2.SendTask( - [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }, - RTC_FROM_HERE); + [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); }); +} + +TEST(SequenceCheckerTest, ExpectationToString) { + TaskQueueForTest queue1; + + SequenceChecker sequence_checker; + sequence_checker.Detach(); + + rtc::Event blocker; + queue1.PostTask([&blocker, &sequence_checker]() { + (void)sequence_checker.IsCurrent(); + blocker.Set(); + }); + + blocker.Wait(rtc::Event::kForever); + +#if RTC_DCHECK_IS_ON + + EXPECT_THAT(ExpectationToString(&sequence_checker), + HasSubstr("# Expected: TQ:")); + + // Test for the base class + webrtc_sequence_checker_internal::SequenceCheckerImpl* sequence_checker_base = + &sequence_checker; + EXPECT_THAT(ExpectationToString(sequence_checker_base), + HasSubstr("# Expected: TQ:")); + +#else + GTEST_ASSERT_EQ(ExpectationToString(&sequence_checker), ""); +#endif } class TestAnnotations { @@ -146,7 +173,7 @@ TEST(SequenceCheckerTest, TestAnnotations) { void TestAnnotationsOnWrongQueue() { TestAnnotations annotations; TaskQueueForTest queue; - queue.SendTask([&] { annotations.ModifyTestVar(); }, RTC_FROM_HERE); + queue.SendTask([&] { annotations.ModifyTestVar(); }); } #if RTC_DCHECK_IS_ON diff --git a/api/stats/rtc_stats.h b/api/stats/rtc_stats.h index a5fae52c29..e38373a921 100644 --- a/api/stats/rtc_stats.h +++ b/api/stats/rtc_stats.h @@ -20,6 +20,8 @@ #include #include +#include "absl/types/optional.h" +#include "api/units/timestamp.h" #include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/system/rtc_export_template.h" @@ -41,7 +43,7 @@ class RTCStatsMemberInterface; // Derived classes list their dictionary members, RTCStatsMember, as public // fields, allowing the following: // -// RTCFooStats foo("fooId", GetCurrentTime()); +// RTCFooStats foo("fooId", Timestamp::Micros(GetCurrentTime())); // foo.bar = 42; // foo.baz = std::vector(); // foo.baz->push_back("hello world"); @@ -54,17 +56,17 @@ class RTCStatsMemberInterface; // } class RTC_EXPORT RTCStats { public: - RTCStats(const std::string& id, int64_t timestamp_us) - : id_(id), timestamp_us_(timestamp_us) {} - RTCStats(std::string&& id, int64_t timestamp_us) - : id_(std::move(id)), timestamp_us_(timestamp_us) {} + RTCStats(const std::string& id, Timestamp timestamp) + : id_(id), timestamp_(timestamp) {} + virtual ~RTCStats() {} virtual std::unique_ptr copy() const = 0; const std::string& id() const { return id_; } // Time relative to the UNIX epoch (Jan 1, 1970, UTC), in microseconds. - int64_t timestamp_us() const { return timestamp_us_; } + Timestamp timestamp() const { return timestamp_; } + // Returns the static member variable `kType` of the implementing class. virtual const char* type() const = 0; // Returns a vector of pointers to all the `RTCStatsMemberInterface` members @@ -98,7 +100,7 @@ class RTC_EXPORT RTCStats { MembersOfThisObjectAndAncestors(size_t additional_capacity) const; std::string const id_; - int64_t timestamp_us_; + Timestamp timestamp_; }; // All `RTCStats` classes should use these macros. @@ -125,7 +127,7 @@ class RTC_EXPORT RTCStats { // public: // WEBRTC_RTCSTATS_DECL(); // -// RTCFooStats(const std::string& id, int64_t timestamp_us); +// RTCFooStats(const std::string& id, Timestamp timestamp); // // RTCStatsMember foo; // RTCStatsMember bar; @@ -136,8 +138,8 @@ class RTC_EXPORT RTCStats { // &foo, // &bar); // -// RTCFooStats::RTCFooStats(const std::string& id, int64_t timestamp_us) -// : RTCStats(id, timestamp_us), +// RTCFooStats::RTCFooStats(const std::string& id, Timestamp timestamp) +// : RTCStats(id, timestamp), // foo("foo"), // bar("bar") { // } @@ -158,7 +160,7 @@ class RTC_EXPORT RTCStats { const char this_class::kType[] = type_str; \ \ std::unique_ptr this_class::copy() const { \ - return std::unique_ptr(new this_class(*this)); \ + return std::make_unique(*this); \ } \ \ const char* this_class::type() const { return this_class::kType; } \ @@ -189,7 +191,7 @@ class RTC_EXPORT RTCStats { const char this_class::kType[] = type_str; \ \ std::unique_ptr this_class::copy() const { \ - return std::unique_ptr(new this_class(*this)); \ + return std::make_unique(*this); \ } \ \ const char* this_class::type() const { return this_class::kType; } \ @@ -215,6 +217,19 @@ enum class NonStandardGroupId { kRtcStatsRelativePacketArrivalDelay, }; +// Certain stat members should only be exposed to the JavaScript API in +// certain circumstances as to avoid passive fingerprinting. +enum class StatExposureCriteria : uint8_t { + // The stat should always be exposed. This is the default. + kAlways, + // The stat exposes hardware capabilities and thus should has limited exposure + // to JavaScript. The requirements for exposure are written in the spec at + // https://w3c.github.io/webrtc-stats/#limiting-exposure-of-hardware-capabilities. + kHardwareCapability, + // The stat is non-standard so user agents should filter these. + kNonStandard, +}; + // Interface for `RTCStats` members, which have a name and a value of a type // defined in a subclass. Only the types listed in `Type` are supported, these // are implemented by `RTCStatsMember`. The value of a member may be @@ -249,16 +264,26 @@ class RTCStatsMemberInterface { virtual Type type() const = 0; virtual bool is_sequence() const = 0; virtual bool is_string() const = 0; - bool is_defined() const { return is_defined_; } + virtual bool is_defined() const = 0; // Is this part of the stats spec? Used so that chromium can easily filter // out anything unstandardized. - virtual bool is_standardized() const = 0; + bool is_standardized() const { + return exposure_criteria() != StatExposureCriteria::kNonStandard; + } // Non-standard stats members can have group IDs in order to be exposed in // JavaScript through experiments. Standardized stats have no group IDs. virtual std::vector group_ids() const { return {}; } + // The conditions for exposing the statistic to JavaScript. Stats with + // criteria that is not kAlways has some restriction and should be filtered + // in accordance to the spec. + virtual StatExposureCriteria exposure_criteria() const { + return StatExposureCriteria::kAlways; + } // Type and value comparator. The names are not compared. These operators are // exposed for testing. - virtual bool operator==(const RTCStatsMemberInterface& other) const = 0; + bool operator==(const RTCStatsMemberInterface& other) const { + return IsEqual(other); + } bool operator!=(const RTCStatsMemberInterface& other) const { return !(*this == other); } @@ -277,11 +302,11 @@ class RTCStatsMemberInterface { } protected: - RTCStatsMemberInterface(const char* name, bool is_defined) - : name_(name), is_defined_(is_defined) {} + explicit RTCStatsMemberInterface(const char* name) : name_(name) {} + + virtual bool IsEqual(const RTCStatsMemberInterface& other) const = 0; const char* const name_; - bool is_defined_; }; // Template implementation of `RTCStatsMemberInterface`. @@ -291,80 +316,72 @@ template class RTCStatsMember : public RTCStatsMemberInterface { public: explicit RTCStatsMember(const char* name) - : RTCStatsMemberInterface(name, /*is_defined=*/false), value_() {} + : RTCStatsMemberInterface(name), value_() {} RTCStatsMember(const char* name, const T& value) - : RTCStatsMemberInterface(name, /*is_defined=*/true), value_(value) {} + : RTCStatsMemberInterface(name), value_(value) {} RTCStatsMember(const char* name, T&& value) - : RTCStatsMemberInterface(name, /*is_defined=*/true), - value_(std::move(value)) {} + : RTCStatsMemberInterface(name), value_(std::move(value)) {} explicit RTCStatsMember(const RTCStatsMember& other) - : RTCStatsMemberInterface(other.name_, other.is_defined_), - value_(other.value_) {} + : RTCStatsMemberInterface(other.name_), value_(other.value_) {} explicit RTCStatsMember(RTCStatsMember&& other) - : RTCStatsMemberInterface(other.name_, other.is_defined_), - value_(std::move(other.value_)) {} + : RTCStatsMemberInterface(other.name_), value_(std::move(other.value_)) {} static Type StaticType(); Type type() const override { return StaticType(); } bool is_sequence() const override; bool is_string() const override; - bool is_standardized() const override { return true; } - bool operator==(const RTCStatsMemberInterface& other) const override { - if (type() != other.type() || is_standardized() != other.is_standardized()) - return false; - const RTCStatsMember& other_t = - static_cast&>(other); - if (!is_defined_) - return !other_t.is_defined(); - if (!other.is_defined()) - return false; - return value_ == other_t.value_; - } + bool is_defined() const override { return value_.has_value(); } std::string ValueToString() const override; std::string ValueToJson() const override; template inline T ValueOrDefault(U default_value) const { - if (is_defined()) { - return *(*this); - } - return default_value; + return value_.value_or(default_value); } // Assignment operators. T& operator=(const T& value) { value_ = value; - is_defined_ = true; - return value_; + return value_.value(); } T& operator=(const T&& value) { value_ = std::move(value); - is_defined_ = true; - return value_; + return value_.value(); } // Value getters. T& operator*() { - RTC_DCHECK(is_defined_); - return value_; + RTC_DCHECK(value_); + return *value_; } const T& operator*() const { - RTC_DCHECK(is_defined_); - return value_; + RTC_DCHECK(value_); + return *value_; } // Value getters, arrow operator. T* operator->() { - RTC_DCHECK(is_defined_); - return &value_; + RTC_DCHECK(value_); + return &(*value_); } const T* operator->() const { - RTC_DCHECK(is_defined_); - return &value_; + RTC_DCHECK(value_); + return &(*value_); + } + + protected: + bool IsEqual(const RTCStatsMemberInterface& other) const override { + if (type() != other.type() || + is_standardized() != other.is_standardized() || + exposure_criteria() != other.exposure_criteria()) + return false; + const RTCStatsMember& other_t = + static_cast&>(other); + return value_ == other_t.value_; } private: - T value_; + absl::optional value_; }; namespace rtc_stats_internal { @@ -405,38 +422,118 @@ WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector); WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64); WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble); +// For stats with restricted exposure. +template +class RTCRestrictedStatsMember : public RTCStatsMember { + public: + explicit RTCRestrictedStatsMember(const char* name) + : RTCStatsMember(name) {} + RTCRestrictedStatsMember(const char* name, const T& value) + : RTCStatsMember(name, value) {} + RTCRestrictedStatsMember(const char* name, T&& value) + : RTCStatsMember(name, std::move(value)) {} + RTCRestrictedStatsMember(const RTCRestrictedStatsMember& other) + : RTCStatsMember(other) {} + RTCRestrictedStatsMember(RTCRestrictedStatsMember&& other) + : RTCStatsMember(std::move(other)) {} + + StatExposureCriteria exposure_criteria() const override { return E; } + + T& operator=(const T& value) { return RTCStatsMember::operator=(value); } + T& operator=(const T&& value) { + return RTCStatsMember::operator=(std::move(value)); + } + + private: + static_assert(E != StatExposureCriteria::kAlways, + "kAlways is the default exposure criteria. Use " + "RTCStatMember instead."); +}; + +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; + // Using inheritance just so that it's obvious from the member's declaration // whether it's standardized or not. template -class RTCNonStandardStatsMember : public RTCStatsMember { +class RTCNonStandardStatsMember + : public RTCRestrictedStatsMember { public: explicit RTCNonStandardStatsMember(const char* name) - : RTCStatsMember(name) {} + : RTCRestrictedStatsBase(name) {} RTCNonStandardStatsMember(const char* name, std::initializer_list group_ids) - : RTCStatsMember(name), group_ids_(group_ids) {} + : RTCRestrictedStatsBase(name), group_ids_(group_ids) {} RTCNonStandardStatsMember(const char* name, const T& value) - : RTCStatsMember(name, value) {} + : RTCRestrictedStatsBase(name, value) {} RTCNonStandardStatsMember(const char* name, T&& value) - : RTCStatsMember(name, std::move(value)) {} - explicit RTCNonStandardStatsMember(const RTCNonStandardStatsMember& other) - : RTCStatsMember(other), group_ids_(other.group_ids_) {} - explicit RTCNonStandardStatsMember(RTCNonStandardStatsMember&& other) - : RTCStatsMember(std::move(other)), + : RTCRestrictedStatsBase(name, std::move(value)) {} + RTCNonStandardStatsMember(const RTCNonStandardStatsMember& other) + : RTCRestrictedStatsBase(other), group_ids_(other.group_ids_) {} + RTCNonStandardStatsMember(RTCNonStandardStatsMember&& other) + : RTCRestrictedStatsBase(std::move(other)), group_ids_(std::move(other.group_ids_)) {} - bool is_standardized() const override { return false; } - std::vector group_ids() const override { return group_ids_; } - T& operator=(const T& value) { return RTCStatsMember::operator=(value); } + T& operator=(const T& value) { + return RTCRestrictedStatsMember< + T, StatExposureCriteria::kNonStandard>::operator=(value); + } T& operator=(const T&& value) { - return RTCStatsMember::operator=(std::move(value)); + return RTCRestrictedStatsMember< + T, StatExposureCriteria::kNonStandard>::operator=(std::move(value)); } private: + using RTCRestrictedStatsBase = + RTCRestrictedStatsMember; std::vector group_ids_; }; diff --git a/api/stats/rtc_stats_report.h b/api/stats/rtc_stats_report.h index 2ced422370..8678eb35bf 100644 --- a/api/stats/rtc_stats_report.h +++ b/api/stats/rtc_stats_report.h @@ -17,11 +17,13 @@ #include #include #include +#include #include #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "api/stats/rtc_stats.h" +#include "api/units/timestamp.h" // TODO(tommi): Remove this include after fixing iwyu issue in chromium. // See: third_party/blink/renderer/platform/peerconnection/rtc_stats.cc #include "rtc_base/ref_counted_object.h" @@ -58,16 +60,38 @@ class RTC_EXPORT RTCStatsReport final StatsMap::const_iterator it_; }; - // TODO(hbos): Remove "= 0" once Chromium unittest has been updated to call - // with a parameter. crbug.com/627816 + // TODO(bugs.webrtc.org/13756): deprecate this in favor of Timestamp. + // TODO(hbos): Remove "= 0" once downstream has been updated to call with a + // parameter. + ABSL_DEPRECATED("Call Create with Timestamp instead") static rtc::scoped_refptr Create(int64_t timestamp_us = 0); + static rtc::scoped_refptr Create(Timestamp timestamp); + // TODO(bugs.webrtc.org/13756): deprecate this in favor of Timestamp. + ABSL_DEPRECATED("Use constructor with Timestamp instead") explicit RTCStatsReport(int64_t timestamp_us); + explicit RTCStatsReport(Timestamp timestamp); + RTCStatsReport(const RTCStatsReport& other) = delete; rtc::scoped_refptr Copy() const; - int64_t timestamp_us() const { return timestamp_us_; } + // TODO(bugs.webrtc.org/13756): deprecate this in favor of Timestamp. + ABSL_DEPRECATED("Call timestamp() instead") + int64_t timestamp_us() const { return timestamp_.us_or(-1); } + Timestamp timestamp() const { return timestamp_; } void AddStats(std::unique_ptr stats); + // On success, returns a non-owning pointer to `stats`. If the stats ID is not + // unique, `stats` is not inserted and nullptr is returned. + template + T* TryAddStats(std::unique_ptr stats) { + T* stats_ptr = stats.get(); + if (!stats_ + .insert(std::make_pair(std::string(stats->id()), std::move(stats))) + .second) { + return nullptr; + } + return stats_ptr; + } const RTCStats* Get(const std::string& id) const; size_t size() const { return stats_.size(); } @@ -115,7 +139,7 @@ class RTC_EXPORT RTCStatsReport final ~RTCStatsReport() = default; private: - int64_t timestamp_us_; + Timestamp timestamp_; StatsMap stats_; }; diff --git a/api/stats/rtcstats_objects.h b/api/stats/rtcstats_objects.h index 14c5f24a2c..8e376a3805 100644 --- a/api/stats/rtcstats_objects.h +++ b/api/stats/rtcstats_objects.h @@ -120,8 +120,7 @@ class RTC_EXPORT RTCCertificateStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCCertificateStats(const std::string& id, int64_t timestamp_us); - RTCCertificateStats(std::string&& id, int64_t timestamp_us); + RTCCertificateStats(std::string id, Timestamp timestamp); RTCCertificateStats(const RTCCertificateStats& other); ~RTCCertificateStats() override; @@ -150,8 +149,7 @@ class RTC_EXPORT RTCCodecStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCCodecStats(const std::string& id, int64_t timestamp_us); - RTCCodecStats(std::string&& id, int64_t timestamp_us); + RTCCodecStats(std::string id, Timestamp timestamp); RTCCodecStats(const RTCCodecStats& other); ~RTCCodecStats() override; @@ -168,15 +166,14 @@ class RTC_EXPORT RTCDataChannelStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCDataChannelStats(const std::string& id, int64_t timestamp_us); - RTCDataChannelStats(std::string&& id, int64_t timestamp_us); + RTCDataChannelStats(std::string id, Timestamp timestamp); RTCDataChannelStats(const RTCDataChannelStats& other); ~RTCDataChannelStats() override; RTCStatsMember label; RTCStatsMember protocol; RTCStatsMember data_channel_identifier; - // TODO(hbos): Support enum types? "RTCStatsMember"? + // Enum type RTCDataChannelState. RTCStatsMember state; RTCStatsMember messages_sent; RTCStatsMember bytes_sent; @@ -185,30 +182,27 @@ class RTC_EXPORT RTCDataChannelStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#candidatepair-dict* -// TODO(hbos): Tracking bug https://bugs.webrtc.org/7062 class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCIceCandidatePairStats(const std::string& id, int64_t timestamp_us); - RTCIceCandidatePairStats(std::string&& id, int64_t timestamp_us); + RTCIceCandidatePairStats(std::string id, Timestamp timestamp); RTCIceCandidatePairStats(const RTCIceCandidatePairStats& other); ~RTCIceCandidatePairStats() override; RTCStatsMember transport_id; RTCStatsMember local_candidate_id; RTCStatsMember remote_candidate_id; - // TODO(hbos): Support enum types? - // "RTCStatsMember"? + // Enum type RTCStatsIceCandidatePairState. RTCStatsMember state; // Obsolete: priority RTCStatsMember priority; RTCStatsMember nominated; - // TODO(hbos): Collect this the way the spec describes it. We have a value for - // it but it is not spec-compliant. https://bugs.webrtc.org/7062 + // `writable` does not exist in the spec and old comments suggest it used to + // exist but was incorrectly implemented. + // TODO(https://crbug.com/webrtc/14171): Standardize and/or modify + // implementation. RTCStatsMember writable; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember readable; RTCStatsMember packets_sent; RTCStatsMember packets_received; RTCStatsMember bytes_sent; @@ -216,35 +210,19 @@ class RTC_EXPORT RTCIceCandidatePairStats final : public RTCStats { RTCStatsMember total_round_trip_time; RTCStatsMember current_round_trip_time; RTCStatsMember available_outgoing_bitrate; - // TODO(hbos): Populate this value. It is wired up and collected the same way - // "VideoBwe.googAvailableReceiveBandwidth" is, but that value is always - // undefined. https://bugs.webrtc.org/7062 RTCStatsMember available_incoming_bitrate; RTCStatsMember requests_received; RTCStatsMember requests_sent; RTCStatsMember responses_received; RTCStatsMember responses_sent; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember retransmissions_received; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember retransmissions_sent; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember consent_requests_received; RTCStatsMember consent_requests_sent; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember consent_responses_received; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7062 - RTCStatsMember consent_responses_sent; RTCStatsMember packets_discarded_on_send; RTCStatsMember bytes_discarded_on_send; + RTCStatsMember last_packet_received_timestamp; + RTCStatsMember last_packet_sent_timestamp; }; // https://w3c.github.io/webrtc-stats/#icecandidate-dict* -// TODO(hbos): `RTCStatsCollector` only collects candidates that are part of -// ice candidate pairs, but there could be candidates not paired with anything. -// crbug.com/632723 -// TODO(qingsi): Add the stats of STUN binding requests (keepalives) and collect -// them in the new PeerConnection::GetStats. class RTC_EXPORT RTCIceCandidateStats : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -261,19 +239,22 @@ class RTC_EXPORT RTCIceCandidateStats : public RTCStats { RTCStatsMember port; RTCStatsMember protocol; RTCStatsMember relay_protocol; - // TODO(hbos): Support enum types? "RTCStatsMember"? + // Enum type RTCIceCandidateType. RTCStatsMember candidate_type; RTCStatsMember priority; RTCStatsMember url; + RTCStatsMember foundation; + RTCStatsMember related_address; + RTCStatsMember related_port; + RTCStatsMember username_fragment; + // Enum type RTCIceTcpCandidateType. + RTCStatsMember tcp_type; RTCNonStandardStatsMember vpn; RTCNonStandardStatsMember network_adapter_type; protected: - RTCIceCandidateStats(const std::string& id, - int64_t timestamp_us, - bool is_remote); - RTCIceCandidateStats(std::string&& id, int64_t timestamp_us, bool is_remote); + RTCIceCandidateStats(std::string id, Timestamp timestamp, bool is_remote); }; // In the spec both local and remote varieties are of type RTCIceCandidateStats. @@ -284,8 +265,7 @@ class RTC_EXPORT RTCIceCandidateStats : public RTCStats { class RTC_EXPORT RTCLocalIceCandidateStats final : public RTCIceCandidateStats { public: static const char kType[]; - RTCLocalIceCandidateStats(const std::string& id, int64_t timestamp_us); - RTCLocalIceCandidateStats(std::string&& id, int64_t timestamp_us); + RTCLocalIceCandidateStats(std::string id, Timestamp timestamp); std::unique_ptr copy() const override; const char* type() const override; }; @@ -294,69 +274,56 @@ class RTC_EXPORT RTCRemoteIceCandidateStats final : public RTCIceCandidateStats { public: static const char kType[]; - RTCRemoteIceCandidateStats(const std::string& id, int64_t timestamp_us); - RTCRemoteIceCandidateStats(std::string&& id, int64_t timestamp_us); + RTCRemoteIceCandidateStats(std::string id, Timestamp timestamp); std::unique_ptr copy() const override; const char* type() const override; }; -// https://w3c.github.io/webrtc-stats/#msstats-dict* -// TODO(hbos): Tracking bug crbug.com/660827 -class RTC_EXPORT RTCMediaStreamStats final : public RTCStats { +// TODO(https://crbug.com/webrtc/14419): Delete this class, it's deprecated. +class RTC_EXPORT DEPRECATED_RTCMediaStreamStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCMediaStreamStats(const std::string& id, int64_t timestamp_us); - RTCMediaStreamStats(std::string&& id, int64_t timestamp_us); - RTCMediaStreamStats(const RTCMediaStreamStats& other); - ~RTCMediaStreamStats() override; + DEPRECATED_RTCMediaStreamStats(std::string id, Timestamp timestamp); + DEPRECATED_RTCMediaStreamStats(const DEPRECATED_RTCMediaStreamStats& other); + ~DEPRECATED_RTCMediaStreamStats() override; RTCStatsMember stream_identifier; RTCStatsMember> track_ids; }; +using RTCMediaStreamStats [[deprecated("bugs.webrtc.org/14419")]] = + DEPRECATED_RTCMediaStreamStats; -// https://w3c.github.io/webrtc-stats/#mststats-dict* -// TODO(hbos): Tracking bug crbug.com/659137 -class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats { +// TODO(https://crbug.com/webrtc/14175): Delete this class, it's deprecated. +class RTC_EXPORT DEPRECATED_RTCMediaStreamTrackStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCMediaStreamTrackStats(const std::string& id, - int64_t timestamp_us, - const char* kind); - RTCMediaStreamTrackStats(std::string&& id, - int64_t timestamp_us, - const char* kind); - RTCMediaStreamTrackStats(const RTCMediaStreamTrackStats& other); - ~RTCMediaStreamTrackStats() override; + DEPRECATED_RTCMediaStreamTrackStats(std::string id, + Timestamp timestamp, + const char* kind); + DEPRECATED_RTCMediaStreamTrackStats( + const DEPRECATED_RTCMediaStreamTrackStats& other); + ~DEPRECATED_RTCMediaStreamTrackStats() override; RTCStatsMember track_identifier; RTCStatsMember media_source_id; RTCStatsMember remote_source; RTCStatsMember ended; - // TODO(hbos): `RTCStatsCollector` does not return stats for detached tracks. - // crbug.com/659137 + // TODO(https://crbug.com/webrtc/14173): Remove this obsolete metric. RTCStatsMember detached; - // See `RTCMediaStreamTrackKind` for valid values. + // Enum type RTCMediaStreamTrackKind. RTCStatsMember kind; RTCStatsMember jitter_buffer_delay; RTCStatsMember jitter_buffer_emitted_count; // Video-only members RTCStatsMember frame_width; RTCStatsMember frame_height; - // TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/659137 - RTCStatsMember frames_per_second; RTCStatsMember frames_sent; RTCStatsMember huge_frames_sent; RTCStatsMember frames_received; RTCStatsMember frames_decoded; RTCStatsMember frames_dropped; - // TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/659137 - RTCStatsMember frames_corrupted; - // TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/659137 - RTCStatsMember partial_frames_lost; - // TODO(hbos): Not collected by `RTCStatsCollector`. crbug.com/659137 - RTCStatsMember full_frames_lost; // Audio-only members RTCStatsMember audio_level; // Receive-only RTCStatsMember total_audio_energy; // Receive-only @@ -369,40 +336,16 @@ class RTC_EXPORT RTCMediaStreamTrackStats final : public RTCStats { RTCStatsMember concealment_events; RTCStatsMember inserted_samples_for_deceleration; RTCStatsMember removed_samples_for_acceleration; - // Non-standard audio-only member - // TODO(kuddai): Add description to standard. crbug.com/webrtc/10042 - RTCNonStandardStatsMember jitter_buffer_flushes; - RTCNonStandardStatsMember delayed_packet_outage_samples; - RTCNonStandardStatsMember relative_packet_arrival_delay; - // Non-standard metric showing target delay of jitter buffer. - // This value is increased by the target jitter buffer delay every time a - // sample is emitted by the jitter buffer. The added target is the target - // delay, in seconds, at the time that the sample was emitted from the jitter - // buffer. (https://github.com/w3c/webrtc-provisional-stats/pull/20) - // Currently it is implemented only for audio. - // TODO(titovartem) implement for video streams when will be requested. - RTCNonStandardStatsMember jitter_buffer_target_delay; - // TODO(henrik.lundin): Add description of the interruption metrics at - // https://github.com/henbos/webrtc-provisional-stats/issues/17 - RTCNonStandardStatsMember interruption_count; - RTCNonStandardStatsMember total_interruption_duration; - // Non-standard video-only members. - // https://henbos.github.io/webrtc-provisional-stats/#RTCVideoReceiverStats-dict* - RTCNonStandardStatsMember freeze_count; - RTCNonStandardStatsMember pause_count; - RTCNonStandardStatsMember total_freezes_duration; - RTCNonStandardStatsMember total_pauses_duration; - RTCNonStandardStatsMember total_frames_duration; - RTCNonStandardStatsMember sum_squared_frame_durations; }; +using RTCMediaStreamTrackStats [[deprecated("bugs.webrtc.org/14175")]] = + DEPRECATED_RTCMediaStreamTrackStats; // https://w3c.github.io/webrtc-stats/#pcstats-dict* class RTC_EXPORT RTCPeerConnectionStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCPeerConnectionStats(const std::string& id, int64_t timestamp_us); - RTCPeerConnectionStats(std::string&& id, int64_t timestamp_us); + RTCPeerConnectionStats(std::string id, Timestamp timestamp); RTCPeerConnectionStats(const RTCPeerConnectionStats& other); ~RTCPeerConnectionStats() override; @@ -411,7 +354,6 @@ class RTC_EXPORT RTCPeerConnectionStats final : public RTCStats { }; // https://w3c.github.io/webrtc-stats/#streamstats-dict* -// TODO(hbos): Tracking bug crbug.com/657854 class RTC_EXPORT RTCRTPStreamStats : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); @@ -430,8 +372,7 @@ class RTC_EXPORT RTCRTPStreamStats : public RTCStats { RTCStatsMember media_type; // renamed to kind. protected: - RTCRTPStreamStats(const std::string& id, int64_t timestamp_us); - RTCRTPStreamStats(std::string&& id, int64_t timestamp_us); + RTCRTPStreamStats(std::string id, Timestamp timestamp); }; // https://www.w3.org/TR/webrtc-stats/#receivedrtpstats-dict* @@ -442,20 +383,11 @@ class RTC_EXPORT RTCReceivedRtpStreamStats : public RTCRTPStreamStats { RTCReceivedRtpStreamStats(const RTCReceivedRtpStreamStats& other); ~RTCReceivedRtpStreamStats() override; - // TODO(hbos) The following fields need to be added and migrated - // both from RTCInboundRtpStreamStats and RTCRemoteInboundRtpStreamStats: - // packetsReceived, packetsRepaired, burstPacketsLost, - // burstPacketDiscarded, burstLossCount, burstDiscardCount, burstLossRate, - // burstDiscardRate, gapLossRate, gapDiscardRate, framesDropped, - // partialFramesLost, fullFramesLost - // crbug.com/webrtc/12532 RTCStatsMember jitter; RTCStatsMember packets_lost; // Signed per RFC 3550 - RTCStatsMember packets_discarded; protected: - RTCReceivedRtpStreamStats(const std::string&& id, int64_t timestamp_us); - RTCReceivedRtpStreamStats(std::string&& id, int64_t timestamp_us); + RTCReceivedRtpStreamStats(std::string id, Timestamp timestamp); }; // https://www.w3.org/TR/webrtc-stats/#sentrtpstats-dict* @@ -470,31 +402,35 @@ class RTC_EXPORT RTCSentRtpStreamStats : public RTCRTPStreamStats { RTCStatsMember bytes_sent; protected: - RTCSentRtpStreamStats(const std::string&& id, int64_t timestamp_us); - RTCSentRtpStreamStats(std::string&& id, int64_t timestamp_us); + RTCSentRtpStreamStats(std::string id, Timestamp timestamp); }; // https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict* -// TODO(hbos): Support the remote case |is_remote = true|. -// https://bugs.webrtc.org/7065 class RTC_EXPORT RTCInboundRTPStreamStats final : public RTCReceivedRtpStreamStats { public: WEBRTC_RTCSTATS_DECL(); - RTCInboundRTPStreamStats(const std::string& id, int64_t timestamp_us); - RTCInboundRTPStreamStats(std::string&& id, int64_t timestamp_us); + RTCInboundRTPStreamStats(std::string id, Timestamp timestamp); RTCInboundRTPStreamStats(const RTCInboundRTPStreamStats& other); ~RTCInboundRTPStreamStats() override; + // TODO(https://crbug.com/webrtc/14174): Implement trackIdentifier and kind. + + RTCStatsMember playout_id; + RTCStatsMember track_identifier; + RTCStatsMember mid; RTCStatsMember remote_id; RTCStatsMember packets_received; + RTCStatsMember packets_discarded; RTCStatsMember fec_packets_received; RTCStatsMember fec_packets_discarded; RTCStatsMember bytes_received; RTCStatsMember header_bytes_received; RTCStatsMember last_packet_received_timestamp; RTCStatsMember jitter_buffer_delay; + RTCStatsMember jitter_buffer_target_delay; + RTCStatsMember jitter_buffer_minimum_delay; RTCStatsMember jitter_buffer_emitted_count; RTCStatsMember total_samples_received; RTCStatsMember concealed_samples; @@ -505,74 +441,77 @@ class RTC_EXPORT RTCInboundRTPStreamStats final RTCStatsMember audio_level; RTCStatsMember total_audio_energy; RTCStatsMember total_samples_duration; - // 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 - RTCStatsMember packets_repaired; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_packets_lost; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_packets_discarded; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_loss_count; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_discard_count; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_loss_rate; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - RTCStatsMember burst_discard_rate; - // TODO(hbos): Collect and populate this value. https://bugs.webrtc.org/7065 - 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; RTCStatsMember frames_per_second; RTCStatsMember frames_decoded; RTCStatsMember key_frames_decoded; RTCStatsMember frames_dropped; RTCStatsMember total_decode_time; RTCStatsMember total_processing_delay; + RTCStatsMember total_assembly_time; + RTCStatsMember frames_assembled_from_multiple_packets; RTCStatsMember total_inter_frame_delay; RTCStatsMember total_squared_inter_frame_delay; - // https://henbos.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype + RTCStatsMember pause_count; + RTCStatsMember total_pauses_duration; + RTCStatsMember freeze_count; + RTCStatsMember total_freezes_duration; + // https://w3c.github.io/webrtc-provisional-stats/#dom-rtcinboundrtpstreamstats-contenttype RTCStatsMember content_type; - // TODO(asapersson): Currently only populated if audio/video sync is enabled. + // Only populated if audio/video sync is enabled. + // TODO(https://crbug.com/webrtc/14177): Expose even if A/V sync is off? RTCStatsMember estimated_playout_timestamp; - // TODO(hbos): This is only implemented for video; implement it for audio as - // well. - RTCStatsMember decoder_implementation; + // Only implemented for video. + // TODO(https://crbug.com/webrtc/14178): Also implement for audio. + RTCRestrictedStatsMember + decoder_implementation; // FIR and PLI counts are only defined for |kind == "video"|. RTCStatsMember fir_count; RTCStatsMember pli_count; RTCStatsMember nack_count; RTCStatsMember qp_sum; + // This is a remnant of the legacy getStats() API. When the "video-timing" + // header extension is used, + // https://webrtc.github.io/webrtc-org/experiments/rtp-hdrext/video-timing/, + // `googTimingFrameInfo` is exposed with the value of + // TimingFrameInfo::ToString(). + // TODO(https://crbug.com/webrtc/14586): Unship or standardize this metric. + RTCStatsMember goog_timing_frame_info; + RTCRestrictedStatsMember + power_efficient_decoder; + // Non-standard audio metrics. + RTCNonStandardStatsMember jitter_buffer_flushes; + RTCNonStandardStatsMember delayed_packet_outage_samples; + RTCNonStandardStatsMember relative_packet_arrival_delay; + RTCNonStandardStatsMember interruption_count; + RTCNonStandardStatsMember total_interruption_duration; + + // The former googMinPlayoutDelayMs (in seconds). + RTCNonStandardStatsMember min_playout_delay; }; // https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* -// TODO(hbos): Support the remote case |is_remote = true|. -// https://bugs.webrtc.org/7066 class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { public: WEBRTC_RTCSTATS_DECL(); - RTCOutboundRTPStreamStats(const std::string& id, int64_t timestamp_us); - RTCOutboundRTPStreamStats(std::string&& id, int64_t timestamp_us); + RTCOutboundRTPStreamStats(std::string id, Timestamp timestamp); RTCOutboundRTPStreamStats(const RTCOutboundRTPStreamStats& other); ~RTCOutboundRTPStreamStats() override; RTCStatsMember media_source_id; RTCStatsMember remote_id; + RTCStatsMember mid; RTCStatsMember rid; RTCStatsMember packets_sent; RTCStatsMember retransmitted_packets_sent; RTCStatsMember bytes_sent; RTCStatsMember header_bytes_sent; RTCStatsMember retransmitted_bytes_sent; - // TODO(https://crbug.com/webrtc/13394): Also collect this metric for video. RTCStatsMember target_bitrate; RTCStatsMember frames_encoded; RTCStatsMember key_frames_encoded; @@ -583,24 +522,28 @@ class RTC_EXPORT RTCOutboundRTPStreamStats final : public RTCRTPStreamStats { RTCStatsMember frames_per_second; RTCStatsMember frames_sent; RTCStatsMember huge_frames_sent; - // TODO(https://crbug.com/webrtc/10635): This is only implemented for video; - // implement it for audio as well. RTCStatsMember total_packet_send_delay; // Enum type RTCQualityLimitationReason RTCStatsMember quality_limitation_reason; RTCStatsMember> quality_limitation_durations; // https://w3c.github.io/webrtc-stats/#dom-rtcoutboundrtpstreamstats-qualitylimitationresolutionchanges RTCStatsMember quality_limitation_resolution_changes; - // https://henbos.github.io/webrtc-provisional-stats/#dom-rtcoutboundrtpstreamstats-contenttype + // https://w3c.github.io/webrtc-provisional-stats/#dom-rtcoutboundrtpstreamstats-contenttype RTCStatsMember content_type; - // TODO(hbos): This is only implemented for video; implement it for audio as - // well. - RTCStatsMember encoder_implementation; + // Only implemented for video. + // TODO(https://crbug.com/webrtc/14178): Implement for audio as well. + RTCRestrictedStatsMember + encoder_implementation; // FIR and PLI counts are only defined for |kind == "video"|. RTCStatsMember fir_count; RTCStatsMember pli_count; RTCStatsMember nack_count; RTCStatsMember qp_sum; + RTCStatsMember active; + RTCRestrictedStatsMember + power_efficient_encoder; + RTCStatsMember scalability_mode; }; // https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict* @@ -609,16 +552,10 @@ class RTC_EXPORT RTCRemoteInboundRtpStreamStats final public: WEBRTC_RTCSTATS_DECL(); - RTCRemoteInboundRtpStreamStats(const std::string& id, int64_t timestamp_us); - RTCRemoteInboundRtpStreamStats(std::string&& id, int64_t timestamp_us); + RTCRemoteInboundRtpStreamStats(std::string id, Timestamp timestamp); RTCRemoteInboundRtpStreamStats(const RTCRemoteInboundRtpStreamStats& other); ~RTCRemoteInboundRtpStreamStats() override; - // TODO(hbos): The following RTCReceivedRtpStreamStats metrics should also be - // implemented: packetsReceived, packetsRepaired, - // burstPacketsLost, burstPacketsDiscarded, burstLossCount, burstDiscardCount, - // burstLossRate, burstDiscardRate, gapLossRate and gapDiscardRate. - // RTCRemoteInboundRtpStreamStats RTCStatsMember local_id; RTCStatsMember round_trip_time; RTCStatsMember fraction_lost; @@ -632,8 +569,7 @@ class RTC_EXPORT RTCRemoteOutboundRtpStreamStats final public: WEBRTC_RTCSTATS_DECL(); - RTCRemoteOutboundRtpStreamStats(const std::string& id, int64_t timestamp_us); - RTCRemoteOutboundRtpStreamStats(std::string&& id, int64_t timestamp_us); + RTCRemoteOutboundRtpStreamStats(std::string id, Timestamp timestamp); RTCRemoteOutboundRtpStreamStats(const RTCRemoteOutboundRtpStreamStats& other); ~RTCRemoteOutboundRtpStreamStats() override; @@ -657,8 +593,7 @@ class RTC_EXPORT RTCMediaSourceStats : public RTCStats { RTCStatsMember kind; protected: - RTCMediaSourceStats(const std::string& id, int64_t timestamp_us); - RTCMediaSourceStats(std::string&& id, int64_t timestamp_us); + RTCMediaSourceStats(std::string id, Timestamp timestamp); }; // https://w3c.github.io/webrtc-stats/#dom-rtcaudiosourcestats @@ -666,8 +601,7 @@ class RTC_EXPORT RTCAudioSourceStats final : public RTCMediaSourceStats { public: WEBRTC_RTCSTATS_DECL(); - RTCAudioSourceStats(const std::string& id, int64_t timestamp_us); - RTCAudioSourceStats(std::string&& id, int64_t timestamp_us); + RTCAudioSourceStats(std::string id, Timestamp timestamp); RTCAudioSourceStats(const RTCAudioSourceStats& other); ~RTCAudioSourceStats() override; @@ -683,8 +617,7 @@ class RTC_EXPORT RTCVideoSourceStats final : public RTCMediaSourceStats { public: WEBRTC_RTCSTATS_DECL(); - RTCVideoSourceStats(const std::string& id, int64_t timestamp_us); - RTCVideoSourceStats(std::string&& id, int64_t timestamp_us); + RTCVideoSourceStats(std::string id, Timestamp timestamp); RTCVideoSourceStats(const RTCVideoSourceStats& other); ~RTCVideoSourceStats() override; @@ -699,8 +632,7 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats { public: WEBRTC_RTCSTATS_DECL(); - RTCTransportStats(const std::string& id, int64_t timestamp_us); - RTCTransportStats(std::string&& id, int64_t timestamp_us); + RTCTransportStats(std::string id, Timestamp timestamp); RTCTransportStats(const RTCTransportStats& other); ~RTCTransportStats() override; @@ -709,7 +641,7 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats { RTCStatsMember bytes_received; RTCStatsMember packets_received; RTCStatsMember rtcp_transport_stats_id; - // TODO(hbos): Support enum types? "RTCStatsMember"? + // Enum type RTCDtlsTransportState. RTCStatsMember dtls_state; RTCStatsMember selected_candidate_pair_id; RTCStatsMember local_certificate_id; @@ -724,6 +656,23 @@ class RTC_EXPORT RTCTransportStats final : public RTCStats { RTCStatsMember ice_state; }; +// https://w3c.github.io/webrtc-stats/#playoutstats-dict* +class RTC_EXPORT RTCAudioPlayoutStats final : public RTCStats { + public: + WEBRTC_RTCSTATS_DECL(); + + RTCAudioPlayoutStats(const std::string& id, Timestamp timestamp); + RTCAudioPlayoutStats(const RTCAudioPlayoutStats& other); + ~RTCAudioPlayoutStats() override; + + RTCStatsMember kind; + RTCStatsMember synthesized_samples_duration; + RTCStatsMember synthesized_samples_events; + RTCStatsMember total_samples_duration; + RTCStatsMember total_playout_delay; + RTCStatsMember total_samples_count; +}; + } // namespace webrtc #endif // API_STATS_RTCSTATS_OBJECTS_H_ diff --git a/api/task_queue/BUILD.gn b/api/task_queue/BUILD.gn index 1072057e3f..69393b80ff 100644 --- a/api/task_queue/BUILD.gn +++ b/api/task_queue/BUILD.gn @@ -11,7 +11,6 @@ import("../../webrtc.gni") rtc_library("task_queue") { visibility = [ "*" ] public = [ - "queued_task.h", "task_queue_base.h", "task_queue_factory.h", ] @@ -21,10 +20,12 @@ rtc_library("task_queue") { "../../rtc_base:checks", "../../rtc_base:macromagic", "../../rtc_base/system:rtc_export", + "../units:time_delta", ] absl_deps = [ "//third_party/abseil-cpp/absl/base:config", "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings", ] } @@ -42,9 +43,10 @@ rtc_library("task_queue_test") { visibility = [] visibility = webrtc_default_visibility visibility += [ - # This is the only Chromium target that can depend on this. The reason - # behind this is the fact that this is a 'testonly' target and as such + # This is the only Chromium targets that can depend on this. The reason + # behind this is the fact that these are 'testonly' targets and as such # it cannot be part of the WebRTC component. + "//components/webrtc:unit_tests", "//third_party/blink/renderer/platform:blink_platform_unittests_sources", ] @@ -55,20 +57,23 @@ rtc_library("task_queue_test") { "../../test:test_support", ] absl_deps = [ - "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/cleanup", "//third_party/abseil-cpp/absl/strings", ] } else { deps = [ + ":default_task_queue_factory", ":task_queue", + "../../api:field_trials_view", + "../../api:make_ref_counted", + "../../api/units:time_delta", "../../rtc_base:refcount", "../../rtc_base:rtc_event", "../../rtc_base:timeutils", - "../../rtc_base/task_utils:to_queued_task", "../../test:test_support", ] absl_deps = [ - "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/cleanup", "//third_party/abseil-cpp/absl/strings", ] } @@ -80,11 +85,26 @@ rtc_library("default_task_queue_factory") { poisonous = [ "default_task_queue" ] } sources = [ "default_task_queue_factory.h" ] - deps = [ ":task_queue" ] + deps = [ + ":task_queue", + "../../api:field_trials_view", + "../../rtc_base/memory:always_valid_pointer", + ] if (rtc_enable_libevent) { - sources += [ "default_task_queue_factory_libevent.cc" ] - deps += [ "../../rtc_base:rtc_task_queue_libevent" ] + if (is_android) { + sources += + [ "default_task_queue_factory_stdlib_or_libevent_experiment.cc" ] + deps += [ + "../../api/transport:field_trial_based_config", + "../../rtc_base:logging", + "../../rtc_base:rtc_task_queue_libevent", + "../../rtc_base:rtc_task_queue_stdlib", + ] + } else { + sources += [ "default_task_queue_factory_libevent.cc" ] + deps += [ "../../rtc_base:rtc_task_queue_libevent" ] + } } else if (is_mac || is_ios) { sources += [ "default_task_queue_factory_gcd.cc" ] deps += [ "../../rtc_base:rtc_task_queue_gcd" ] @@ -97,6 +117,22 @@ rtc_library("default_task_queue_factory") { } } +rtc_library("pending_task_safety_flag") { + visibility = [ "*" ] + sources = [ + "pending_task_safety_flag.cc", + "pending_task_safety_flag.h", + ] + deps = [ + "../../api:refcountedbase", + "../../api:scoped_refptr", + "../../api:sequence_checker", + "../../rtc_base:checks", + "../../rtc_base/system:no_unique_address", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/functional:any_invocable" ] +} + if (rtc_include_tests) { rtc_library("task_queue_default_factory_unittests") { testonly = true @@ -107,4 +143,17 @@ if (rtc_include_tests) { "../../test:test_support", ] } + + rtc_library("pending_task_safety_flag_unittests") { + testonly = true + sources = [ "pending_task_safety_flag_unittest.cc" ] + deps = [ + ":pending_task_safety_flag", + "../../rtc_base:logging", + "../../rtc_base:rtc_event", + "../../rtc_base:rtc_task_queue", + "../../rtc_base:task_queue_for_test", + "../../test:test_support", + ] + } } diff --git a/api/task_queue/DEPS b/api/task_queue/DEPS index fab6056212..1365edb504 100644 --- a/api/task_queue/DEPS +++ b/api/task_queue/DEPS @@ -7,4 +7,8 @@ specific_include_rules = { "task_queue_test\.h": [ "+test/gtest.h", ], + "pending_task_safety_flag.h": [ + "+rtc_base/checks.h", + "+rtc_base/system/no_unique_address.h", + ], } diff --git a/api/task_queue/default_task_queue_factory.h b/api/task_queue/default_task_queue_factory.h index ccdd1ebec0..1d2dbd7ec2 100644 --- a/api/task_queue/default_task_queue_factory.h +++ b/api/task_queue/default_task_queue_factory.h @@ -12,11 +12,13 @@ #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" namespace webrtc { -std::unique_ptr CreateDefaultTaskQueueFactory(); +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials = nullptr); } // namespace webrtc diff --git a/api/task_queue/default_task_queue_factory_gcd.cc b/api/task_queue/default_task_queue_factory_gcd.cc index 7e17b4846d..391f09b393 100644 --- a/api/task_queue/default_task_queue_factory_gcd.cc +++ b/api/task_queue/default_task_queue_factory_gcd.cc @@ -9,12 +9,14 @@ */ #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "rtc_base/task_queue_gcd.h" namespace webrtc { -std::unique_ptr CreateDefaultTaskQueueFactory() { +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { return CreateTaskQueueGcdFactory(); } diff --git a/api/task_queue/default_task_queue_factory_libevent.cc b/api/task_queue/default_task_queue_factory_libevent.cc index f2fb418fd3..89079f51ca 100644 --- a/api/task_queue/default_task_queue_factory_libevent.cc +++ b/api/task_queue/default_task_queue_factory_libevent.cc @@ -9,12 +9,14 @@ */ #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "rtc_base/task_queue_libevent.h" namespace webrtc { -std::unique_ptr CreateDefaultTaskQueueFactory() { +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { return CreateTaskQueueLibeventFactory(); } diff --git a/api/task_queue/default_task_queue_factory_stdlib.cc b/api/task_queue/default_task_queue_factory_stdlib.cc index ca7d720cbe..10cda7c5ec 100644 --- a/api/task_queue/default_task_queue_factory_stdlib.cc +++ b/api/task_queue/default_task_queue_factory_stdlib.cc @@ -9,12 +9,14 @@ */ #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "rtc_base/task_queue_stdlib.h" namespace webrtc { -std::unique_ptr CreateDefaultTaskQueueFactory() { +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { return CreateTaskQueueStdlibFactory(); } diff --git a/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc b/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc new file mode 100644 index 0000000000..dc6e835907 --- /dev/null +++ b/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc @@ -0,0 +1,38 @@ +/* + * 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 + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/field_trial_based_config.h" +#include "rtc_base/logging.h" +#include "rtc_base/memory/always_valid_pointer.h" +#include "rtc_base/task_queue_libevent.h" +#include "rtc_base/task_queue_stdlib.h" + +namespace webrtc { + +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials_view) { + AlwaysValidPointer field_trials( + field_trials_view); + if (field_trials->IsEnabled("WebRTC-TaskQueue-ReplaceLibeventWithStdlib")) { + RTC_LOG(LS_INFO) << "WebRTC-TaskQueue-ReplaceLibeventWithStdlib: " + << "using TaskQueueStdlibFactory."; + return CreateTaskQueueStdlibFactory(); + } + + RTC_LOG(LS_INFO) << "WebRTC-TaskQueue-ReplaceLibeventWithStdlib: " + << "using TaskQueueLibeventFactory."; + return CreateTaskQueueLibeventFactory(); +} + +} // namespace webrtc diff --git a/api/task_queue/default_task_queue_factory_win.cc b/api/task_queue/default_task_queue_factory_win.cc index 493ea66ea5..e3adc07327 100644 --- a/api/task_queue/default_task_queue_factory_win.cc +++ b/api/task_queue/default_task_queue_factory_win.cc @@ -9,12 +9,14 @@ */ #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "rtc_base/task_queue_win.h" namespace webrtc { -std::unique_ptr CreateDefaultTaskQueueFactory() { +std::unique_ptr CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { return CreateTaskQueueWinFactory(); } diff --git a/rtc_base/task_utils/pending_task_safety_flag.cc b/api/task_queue/pending_task_safety_flag.cc similarity index 96% rename from rtc_base/task_utils/pending_task_safety_flag.cc rename to api/task_queue/pending_task_safety_flag.cc index 8bff213a03..437ce0755d 100644 --- a/rtc_base/task_utils/pending_task_safety_flag.cc +++ b/api/task_queue/pending_task_safety_flag.cc @@ -8,7 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "rtc_base/task_utils/pending_task_safety_flag.h" +#include "api/task_queue/pending_task_safety_flag.h" namespace webrtc { diff --git a/rtc_base/task_utils/pending_task_safety_flag.h b/api/task_queue/pending_task_safety_flag.h similarity index 77% rename from rtc_base/task_utils/pending_task_safety_flag.h rename to api/task_queue/pending_task_safety_flag.h index 58772bcbb1..3b948ca8f1 100644 --- a/rtc_base/task_utils/pending_task_safety_flag.h +++ b/api/task_queue/pending_task_safety_flag.h @@ -8,9 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef RTC_BASE_TASK_UTILS_PENDING_TASK_SAFETY_FLAG_H_ -#define RTC_BASE_TASK_UTILS_PENDING_TASK_SAFETY_FLAG_H_ +#ifndef API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ +#define API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ +#include + +#include "absl/functional/any_invocable.h" #include "api/ref_counted_base.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" @@ -35,25 +38,25 @@ namespace webrtc { // // class ExampleClass { // .... -// my_task_queue_->PostTask(ToQueuedTask( -// [safety = pending_task_safety_flag_, this]() { +// rtc::scoped_refptr flag = safety_flag_; +// my_task_queue_->PostTask( +// [flag = std::move(flag), this] { // // Now running on the main thread. -// if (!safety->alive()) +// if (!flag->alive()) // return; // MyMethod(); -// })); +// }); // .... // ~ExampleClass() { -// pending_task_safety_flag_->SetNotAlive(); +// safety_flag_->SetNotAlive(); // } -// scoped_refptr pending_task_safety_flag_ +// scoped_refptr safety_flag_ // = PendingTaskSafetyFlag::Create(); // } // -// ToQueuedTask has an overload that makes this check automatic: +// SafeTask makes this check automatic: // -// my_task_queue_->PostTask(ToQueuedTask(pending_task_safety_flag_, -// [this]() { MyMethod(); })); +// my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); })); // class PendingTaskSafetyFlag final : public rtc::RefCountedNonVirtual { @@ -103,13 +106,10 @@ class PendingTaskSafetyFlag final // It does automatic PTSF creation and signalling of destruction when the // ScopedTaskSafety instance goes out of scope. // -// ToQueuedTask has an overload that takes a ScopedTaskSafety too, so there -// is no need to explicitly call the "flag" method. -// // Example usage: // -// my_task_queue->PostTask(ToQueuedTask(scoped_task_safety, -// [this]() { +// my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(), +// [this] { // // task goes here // } // @@ -119,11 +119,20 @@ class PendingTaskSafetyFlag final class ScopedTaskSafety final { public: ScopedTaskSafety() = default; + explicit ScopedTaskSafety(rtc::scoped_refptr flag) + : flag_(std::move(flag)) {} ~ScopedTaskSafety() { flag_->SetNotAlive(); } // Returns a new reference to the safety flag. rtc::scoped_refptr flag() const { return flag_; } + // Marks the current flag as not-alive and attaches to a new one. + void reset(rtc::scoped_refptr new_flag = + PendingTaskSafetyFlag::Create()) { + flag_->SetNotAlive(); + flag_ = std::move(new_flag); + } + private: rtc::scoped_refptr flag_ = PendingTaskSafetyFlag::Create(); @@ -144,6 +153,16 @@ class ScopedTaskSafetyDetached final { PendingTaskSafetyFlag::CreateDetached(); }; +inline absl::AnyInvocable SafeTask( + rtc::scoped_refptr flag, + absl::AnyInvocable task) { + return [flag = std::move(flag), task = std::move(task)]() mutable { + if (flag->alive()) { + std::move(task)(); + } + }; +} + } // namespace webrtc -#endif // RTC_BASE_TASK_UTILS_PENDING_TASK_SAFETY_FLAG_H_ +#endif // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ diff --git a/rtc_base/task_utils/pending_task_safety_flag_unittest.cc b/api/task_queue/pending_task_safety_flag_unittest.cc similarity index 71% rename from rtc_base/task_utils/pending_task_safety_flag_unittest.cc rename to api/task_queue/pending_task_safety_flag_unittest.cc index ec5f6a5931..cedf0eb8df 100644 --- a/rtc_base/task_utils/pending_task_safety_flag_unittest.cc +++ b/api/task_queue/pending_task_safety_flag_unittest.cc @@ -8,25 +8,17 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "rtc_base/task_utils/pending_task_safety_flag.h" +#include "api/task_queue/pending_task_safety_flag.h" #include #include "rtc_base/event.h" #include "rtc_base/logging.h" #include "rtc_base/task_queue_for_test.h" -#include "rtc_base/task_utils/to_queued_task.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { -namespace { -using ::testing::AtLeast; -using ::testing::Invoke; -using ::testing::MockFunction; -using ::testing::NiceMock; -using ::testing::Return; -} // namespace TEST(PendingTaskSafetyFlagTest, Basic) { rtc::scoped_refptr safety_flag; @@ -75,11 +67,12 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskSuccess) { void DoStuff() { RTC_DCHECK(!tq_main_->IsCurrent()); - tq_main_->PostTask(ToQueuedTask([safe = flag_, this]() { + rtc::scoped_refptr safe = flag_; + tq_main_->PostTask([safe = std::move(safe), this]() { if (!safe->alive()) return; stuff_done_ = true; - })); + }); } bool stuff_done() const { return stuff_done_; } @@ -87,25 +80,21 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskSuccess) { private: TaskQueueBase* const tq_main_; bool stuff_done_ = false; - rtc::scoped_refptr flag_{ - PendingTaskSafetyFlag::Create()}; + rtc::scoped_refptr flag_ = + PendingTaskSafetyFlag::Create(); }; std::unique_ptr owner; - tq1.SendTask( - [&owner]() { - owner.reset(new Owner()); - EXPECT_FALSE(owner->stuff_done()); - }, - RTC_FROM_HERE); + tq1.SendTask([&owner]() { + owner = std::make_unique(); + EXPECT_FALSE(owner->stuff_done()); + }); ASSERT_TRUE(owner); - tq2.SendTask([&owner]() { owner->DoStuff(); }, RTC_FROM_HERE); - tq1.SendTask( - [&owner]() { - EXPECT_TRUE(owner->stuff_done()); - owner.reset(); - }, - RTC_FROM_HERE); + tq2.SendTask([&owner]() { owner->DoStuff(); }); + tq1.SendTask([&owner]() { + EXPECT_TRUE(owner->stuff_done()); + owner.reset(); + }); ASSERT_FALSE(owner); } @@ -120,14 +109,12 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskDropped) { RTC_DCHECK(tq_main_); *stuff_done_ = false; } - ~Owner() { - RTC_DCHECK(tq_main_->IsCurrent()); - } + ~Owner() { RTC_DCHECK(tq_main_->IsCurrent()); } void DoStuff() { RTC_DCHECK(!tq_main_->IsCurrent()); tq_main_->PostTask( - ToQueuedTask(safety_, [this]() { *stuff_done_ = true; })); + SafeTask(safety_.flag(), [this]() { *stuff_done_ = true; })); } private: @@ -138,8 +125,9 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskDropped) { std::unique_ptr owner; bool stuff_done = false; - tq1.SendTask([&owner, &stuff_done]() { owner.reset(new Owner(&stuff_done)); }, - RTC_FROM_HERE); + tq1.SendTask([&owner, &stuff_done]() { + owner = std::make_unique(&stuff_done); + }); ASSERT_TRUE(owner); // Queue up a task on tq1 that will execute before the 'DoStuff' task // can, and delete the `owner` before the 'stuff' task can execute. @@ -150,7 +138,7 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskDropped) { }); // Queue up a DoStuff... - tq2.SendTask([&owner]() { owner->DoStuff(); }, RTC_FROM_HERE); + tq2.SendTask([&owner]() { owner->DoStuff(); }); ASSERT_TRUE(owner); blocker.Set(); @@ -166,17 +154,35 @@ TEST(PendingTaskSafetyFlagTest, PendingTaskNotAliveInitialized) { // Create a new flag that initially not `alive`. auto flag = PendingTaskSafetyFlag::CreateDetachedInactive(); - tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }, RTC_FROM_HERE); + tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }); bool task_1_ran = false; bool task_2_ran = false; - tq.PostTask(ToQueuedTask(flag, [&task_1_ran]() { task_1_ran = true; })); + tq.PostTask(SafeTask(flag, [&task_1_ran]() { task_1_ran = true; })); tq.PostTask([&flag]() { flag->SetAlive(); }); - tq.PostTask(ToQueuedTask(flag, [&task_2_ran]() { task_2_ran = true; })); + tq.PostTask(SafeTask(flag, [&task_2_ran]() { task_2_ran = true; })); tq.WaitForPreviouslyPostedTasks(); EXPECT_FALSE(task_1_ran); EXPECT_TRUE(task_2_ran); } +TEST(PendingTaskSafetyFlagTest, SafeTask) { + rtc::scoped_refptr flag = + PendingTaskSafetyFlag::Create(); + + int count = 0; + // Create two identical tasks that increment the `count`. + auto task1 = SafeTask(flag, [&count] { ++count; }); + auto task2 = SafeTask(flag, [&count] { ++count; }); + + EXPECT_EQ(count, 0); + std::move(task1)(); + EXPECT_EQ(count, 1); + flag->SetNotAlive(); + // Now task2 should actually not run. + std::move(task2)(); + EXPECT_EQ(count, 1); +} + } // namespace webrtc diff --git a/api/task_queue/queued_task.h b/api/task_queue/queued_task.h deleted file mode 100644 index 27a5eda5a5..0000000000 --- a/api/task_queue/queued_task.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ -#ifndef API_TASK_QUEUE_QUEUED_TASK_H_ -#define API_TASK_QUEUE_QUEUED_TASK_H_ - -namespace webrtc { - -// Base interface for asynchronously executed tasks. -// The interface basically consists of a single function, Run(), that executes -// on the target queue. For more details see the Run() method and TaskQueue. -class QueuedTask { - public: - virtual ~QueuedTask() = default; - - // Main routine that will run when the task is executed on the desired queue. - // The task should return `true` to indicate that it should be deleted or - // `false` to indicate that the queue should consider ownership of the task - // having been transferred. Returning `false` can be useful if a task has - // re-posted itself to a different queue or is otherwise being re-used. - virtual bool Run() = 0; -}; - -} // namespace webrtc - -#endif // API_TASK_QUEUE_QUEUED_TASK_H_ diff --git a/api/task_queue/task_queue_base.cc b/api/task_queue/task_queue_base.cc index 7d3539a63d..ecdc7f7691 100644 --- a/api/task_queue/task_queue_base.cc +++ b/api/task_queue/task_queue_base.cc @@ -11,6 +11,8 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" +#include "absl/functional/any_invocable.h" +#include "api/units/time_delta.h" #include "rtc_base/checks.h" #if defined(ABSL_HAVE_THREAD_LOCAL) diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h index c3e79b72cc..f78600de60 100644 --- a/api/task_queue/task_queue_base.h +++ b/api/task_queue/task_queue_base.h @@ -13,7 +13,8 @@ #include #include -#include "api/task_queue/queued_task.h" +#include "absl/functional/any_invocable.h" +#include "api/units/time_delta.h" #include "rtc_base/system/rtc_export.h" #include "rtc_base/thread_annotations.h" @@ -48,27 +49,31 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // was created on. virtual void Delete() = 0; - // Schedules a task to execute. Tasks are executed in FIFO order. - // If `task->Run()` returns true, task is deleted on the task queue - // before next QueuedTask starts executing. + // Schedules a `task` to execute. Tasks are executed in FIFO order. // When a TaskQueue is deleted, pending tasks will not be executed but they - // will be deleted. The deletion of tasks may happen synchronously on the - // TaskQueue or it may happen asynchronously after TaskQueue is deleted. - // This may vary from one implementation to the next so assumptions about - // lifetimes of pending tasks should not be made. + // will be deleted. + // + // As long as tasks are not posted from task destruction, posted tasks are + // guaranteed to be destroyed with Current() pointing to the task queue they + // were posted to, whether they're executed or not. That means SequenceChecker + // works during task destruction, a fact that can be used to guarantee + // thread-compatible object deletion happening on a particular task queue + // which can simplify class design. + // Note that this guarantee does not apply to delayed tasks. + // // May be called on any thread or task queue, including this task queue. - virtual void PostTask(std::unique_ptr task) = 0; + virtual void PostTask(absl::AnyInvocable task) = 0; // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever // possible. // - // Schedules a task to execute a specified number of milliseconds from when - // the call is made, using "low" precision. All scheduling is affected by - // OS-specific leeway and current workloads which means that in terms of - // precision there are no hard guarantees, but in addition to the OS induced - // leeway, "low" precision adds up to a 17 ms additional leeway. The purpose - // of this leeway is to achieve more efficient CPU scheduling and reduce Idle - // Wake Up frequency. + // Schedules a `task` to execute a specified `delay` from when the call is + // made, using "low" precision. All scheduling is affected by OS-specific + // leeway and current workloads which means that in terms of precision there + // are no hard guarantees, but in addition to the OS induced leeway, "low" + // precision adds up to a 17 ms additional leeway. The purpose of this leeway + // is to achieve more efficient CPU scheduling and reduce Idle Wake Up + // frequency. // // The task may execute with [-1, 17 + OS induced leeway) ms additional delay. // @@ -82,16 +87,16 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // https://crbug.com/webrtc/13583 for more information. // // May be called on any thread or task queue, including this task queue. - virtual void PostDelayedTask(std::unique_ptr task, - uint32_t milliseconds) = 0; + virtual void PostDelayedTask(absl::AnyInvocable task, + TimeDelta delay) = 0; // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever // possible. // - // Schedules a task to execute a specified number of milliseconds from when - // the call is made, using "high" precision. All scheduling is affected by - // OS-specific leeway and current workloads which means that in terms of - // precision there are no hard guarantees. + // Schedules a `task` to execute a specified `delay` from when the call is + // made, using "high" precision. All scheduling is affected by OS-specific + // leeway and current workloads which means that in terms of precision there + // are no hard guarantees. // // The task may execute with [-1, OS induced leeway] ms additional delay. // @@ -101,24 +106,20 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { // battery, when the timer precision can be as poor as 15 ms. // // May be called on any thread or task queue, including this task queue. - virtual void PostDelayedHighPrecisionTask(std::unique_ptr task, - uint32_t milliseconds) { - // Remove default implementation when dependencies have implemented this - // method. - PostDelayedTask(std::move(task), milliseconds); - } + virtual void PostDelayedHighPrecisionTask(absl::AnyInvocable task, + TimeDelta delay) = 0; - // As specified by |precision|, calls either PostDelayedTask() or + // As specified by `precision`, calls either PostDelayedTask() or // PostDelayedHighPrecisionTask(). void PostDelayedTaskWithPrecision(DelayPrecision precision, - std::unique_ptr task, - uint32_t milliseconds) { + absl::AnyInvocable task, + TimeDelta delay) { switch (precision) { case DelayPrecision::kLow: - PostDelayedTask(std::move(task), milliseconds); + PostDelayedTask(std::move(task), delay); break; case DelayPrecision::kHigh: - PostDelayedHighPrecisionTask(std::move(task), milliseconds); + PostDelayedHighPrecisionTask(std::move(task), delay); break; } } diff --git a/api/task_queue/task_queue_test.cc b/api/task_queue/task_queue_test.cc index 3458edb159..7849f4273d 100644 --- a/api/task_queue/task_queue_test.cc +++ b/api/task_queue/task_queue_test.cc @@ -9,16 +9,26 @@ */ #include "api/task_queue/task_queue_test.h" -#include "absl/memory/memory.h" +#include + +#include "absl/cleanup/cleanup.h" #include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" #include "rtc_base/event.h" #include "rtc_base/ref_counter.h" -#include "rtc_base/task_utils/to_queued_task.h" #include "rtc_base/time_utils.h" namespace webrtc { namespace { +// Avoids a dependency to system_wrappers. +void SleepFor(TimeDelta duration) { + rtc::ScopedAllowBaseSyncPrimitivesForTesting allow; + rtc::Event event; + event.Wait(duration); +} + std::unique_ptr CreateTaskQueue( const std::unique_ptr& factory, absl::string_view task_queue_name, @@ -27,13 +37,13 @@ std::unique_ptr CreateTaskQueue( } TEST_P(TaskQueueTest, Construct) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); auto queue = CreateTaskQueue(factory, "Construct"); EXPECT_FALSE(queue->IsCurrent()); } TEST_P(TaskQueueTest, PostAndCheckCurrent) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event event; auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent"); @@ -43,68 +53,65 @@ TEST_P(TaskQueueTest, PostAndCheckCurrent) { // means that TaskQueueBase::Current() will still return a valid value. EXPECT_FALSE(queue->IsCurrent()); - queue->PostTask(ToQueuedTask([&event, &queue] { + queue->PostTask([&event, &queue] { EXPECT_TRUE(queue->IsCurrent()); event.Set(); - })); - EXPECT_TRUE(event.Wait(1000)); + }); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostCustomTask) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event ran; auto queue = CreateTaskQueue(factory, "PostCustomImplementation"); - class CustomTask : public QueuedTask { + class CustomTask { public: explicit CustomTask(rtc::Event* ran) : ran_(ran) {} - private: - bool Run() override { - ran_->Set(); - return false; // Do not allow the task to be deleted by the queue. - } + void operator()() { ran_->Set(); } + private: rtc::Event* const ran_; } my_task(&ran); - queue->PostTask(absl::WrapUnique(&my_task)); - EXPECT_TRUE(ran.Wait(1000)); + queue->PostTask(my_task); + EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostDelayedZero) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event event; auto queue = CreateTaskQueue(factory, "PostDelayedZero"); - queue->PostDelayedTask(ToQueuedTask([&event] { event.Set(); }), 0); - EXPECT_TRUE(event.Wait(1000)); + queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero()); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostFromQueue) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event event; auto queue = CreateTaskQueue(factory, "PostFromQueue"); - queue->PostTask(ToQueuedTask([&event, &queue] { - queue->PostTask(ToQueuedTask([&event] { event.Set(); })); - })); - EXPECT_TRUE(event.Wait(1000)); + queue->PostTask( + [&event, &queue] { queue->PostTask([&event] { event.Set(); }); }); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostDelayed) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event event; auto queue = CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH); int64_t start = rtc::TimeMillis(); - queue->PostDelayedTask(ToQueuedTask([&event, &queue] { - EXPECT_TRUE(queue->IsCurrent()); - event.Set(); - }), - 100); - EXPECT_TRUE(event.Wait(1000)); + queue->PostDelayedTask( + [&event, &queue] { + EXPECT_TRUE(queue->IsCurrent()); + event.Set(); + }, + TimeDelta::Millis(100)); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); int64_t end = rtc::TimeMillis(); // These tests are a little relaxed due to how "powerful" our test bots can // be. Most recently we've seen windows bots fire the callback after 94-99ms, @@ -114,79 +121,123 @@ TEST_P(TaskQueueTest, PostDelayed) { } TEST_P(TaskQueueTest, PostMultipleDelayed) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); auto queue = CreateTaskQueue(factory, "PostMultipleDelayed"); std::vector events(100); for (int i = 0; i < 100; ++i) { rtc::Event* event = &events[i]; - queue->PostDelayedTask(ToQueuedTask([event, &queue] { - EXPECT_TRUE(queue->IsCurrent()); - event->Set(); - }), - i); + queue->PostDelayedTask( + [event, &queue] { + EXPECT_TRUE(queue->IsCurrent()); + event->Set(); + }, + TimeDelta::Millis(i)); } for (rtc::Event& e : events) - EXPECT_TRUE(e.Wait(1000)); + EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostDelayedAfterDestruct) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event run; rtc::Event deleted; auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct"); - queue->PostDelayedTask( - ToQueuedTask([&run] { run.Set(); }, [&deleted] { deleted.Set(); }), 100); + absl::Cleanup cleanup = [&deleted] { deleted.Set(); }; + queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); }, + TimeDelta::Millis(100)); // Destroy the queue. queue = nullptr; // Task might outlive the TaskQueue, but still should be deleted. - EXPECT_TRUE(deleted.Wait(1000)); - EXPECT_FALSE(run.Wait(0)); // and should not run. + EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1))); + EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run. +} + +TEST_P(TaskQueueTest, PostDelayedHighPrecisionAfterDestruct) { + std::unique_ptr factory = GetParam()(nullptr); + rtc::Event run; + rtc::Event deleted; + auto queue = + CreateTaskQueue(factory, "PostDelayedHighPrecisionAfterDestruct"); + absl::Cleanup cleanup = [&deleted] { deleted.Set(); }; + queue->PostDelayedHighPrecisionTask( + [&run, cleanup = std::move(cleanup)] { run.Set(); }, + TimeDelta::Millis(100)); + // Destroy the queue. + queue = nullptr; + // Task might outlive the TaskQueue, but still should be deleted. + EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1))); + EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run. +} + +TEST_P(TaskQueueTest, PostedUnexecutedClosureDestroyedOnTaskQueue) { + std::unique_ptr factory = GetParam()(nullptr); + auto queue = + CreateTaskQueue(factory, "PostedUnexecutedClosureDestroyedOnTaskQueue"); + TaskQueueBase* queue_ptr = queue.get(); + queue->PostTask([] { SleepFor(TimeDelta::Millis(100)); }); + // Give the task queue a chance to start executing the first lambda. + SleepFor(TimeDelta::Millis(10)); + // Then ensure the next lambda (which is likely not executing yet) is + // destroyed in the task queue context when the queue is deleted. + auto cleanup = absl::Cleanup( + [queue_ptr] { EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); }); + queue->PostTask([cleanup = std::move(cleanup)] {}); + queue = nullptr; +} + +TEST_P(TaskQueueTest, PostedExecutedClosureDestroyedOnTaskQueue) { + std::unique_ptr factory = GetParam()(nullptr); + auto queue = + CreateTaskQueue(factory, "PostedExecutedClosureDestroyedOnTaskQueue"); + TaskQueueBase* queue_ptr = queue.get(); + // Ensure an executed lambda is destroyed on the task queue. + rtc::Event finished; + queue->PostTask([cleanup = absl::Cleanup([queue_ptr, &finished] { + EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); + finished.Set(); + })] {}); + finished.Wait(rtc::Event::kForever); } TEST_P(TaskQueueTest, PostAndReuse) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); rtc::Event event; auto post_queue = CreateTaskQueue(factory, "PostQueue"); auto reply_queue = CreateTaskQueue(factory, "ReplyQueue"); int call_count = 0; - class ReusedTask : public QueuedTask { + class ReusedTask { public: ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event) : counter_(*counter), reply_queue_(reply_queue), event_(*event) { EXPECT_EQ(counter_, 0); } + ReusedTask(ReusedTask&&) = default; + ReusedTask& operator=(ReusedTask&&) = delete; - private: - bool Run() override { + void operator()() && { if (++counter_ == 1) { - reply_queue_->PostTask(absl::WrapUnique(this)); - // At this point, the object is owned by reply_queue_ and it's - // theoratically possible that the object has been deleted (e.g. if - // posting wasn't possible). So, don't touch any member variables here. - - // Indicate to the current queue that ownership has been transferred. - return false; + reply_queue_->PostTask(std::move(*this)); + // At this point, the object is in the moved-from state. } else { EXPECT_EQ(counter_, 2); EXPECT_TRUE(reply_queue_->IsCurrent()); event_.Set(); - return true; // Indicate that the object should be deleted. } } + private: int& counter_; TaskQueueBase* const reply_queue_; rtc::Event& event_; }; - auto task = - std::make_unique(&call_count, reply_queue.get(), &event); + ReusedTask task(&call_count, reply_queue.get(), &event); post_queue->PostTask(std::move(task)); - EXPECT_TRUE(event.Wait(1000)); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); } TEST_P(TaskQueueTest, PostALot) { @@ -200,14 +251,14 @@ TEST_P(TaskQueueTest, PostALot) { event_.Set(); } } - bool Wait(int give_up_after_ms) { return event_.Wait(give_up_after_ms); } + bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); } private: webrtc_impl::RefCounter count_; rtc::Event event_; }; - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); static constexpr int kTaskCount = 0xffff; rtc::Event posting_done; BlockingCounter all_destroyed(kTaskCount); @@ -215,16 +266,18 @@ TEST_P(TaskQueueTest, PostALot) { int tasks_executed = 0; auto task_queue = CreateTaskQueue(factory, "PostALot"); - task_queue->PostTask(ToQueuedTask([&] { + task_queue->PostTask([&] { // Post tasks from the queue to guarantee that the 1st task won't be // executed before the last one is posted. for (int i = 0; i < kTaskCount; ++i) { - task_queue->PostTask(ToQueuedTask( - [&] { ++tasks_executed; }, [&] { all_destroyed.DecrementCount(); })); + absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); }; + task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] { + ++tasks_executed; + }); } posting_done.Set(); - })); + }); // Before destroying the task queue wait until all child tasks are posted. posting_done.Wait(rtc::Event::kForever); @@ -234,7 +287,7 @@ TEST_P(TaskQueueTest, PostALot) { // Expect all tasks are destroyed eventually. In some task queue // implementations that might happen on a different thread after task queue is // destroyed. - EXPECT_TRUE(all_destroyed.Wait(60000)); + EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1))); EXPECT_LE(tasks_executed, kTaskCount); } @@ -249,7 +302,7 @@ TEST_P(TaskQueueTest, PostALot) { // unit test, run it under TSan or some other tool that is able to // directly detect data races. TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) { - std::unique_ptr factory = GetParam()(); + std::unique_ptr factory = GetParam()(nullptr); struct SharedState { // First task will set this value to 1 and second will assert it. int state = 0; @@ -257,18 +310,18 @@ TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) { auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState"); rtc::Event done; - queue->PostTask(ToQueuedTask([&state, &queue, &done] { + queue->PostTask([&state, &queue, &done] { // Post tasks from queue to guarantee, that 1st task won't be // executed before the second one will be posted. - queue->PostTask(ToQueuedTask([&state] { state.state = 1; })); - queue->PostTask(ToQueuedTask([&state, &done] { + queue->PostTask([&state] { state.state = 1; }); + queue->PostTask([&state, &done] { EXPECT_EQ(state.state, 1); done.Set(); - })); + }); // Check, that state changing tasks didn't start yet. EXPECT_EQ(state.state, 0); - })); - EXPECT_TRUE(done.Wait(1000)); + }); + EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1))); } // TaskQueueTest is a set of tests for any implementation of the TaskQueueBase. diff --git a/api/task_queue/task_queue_test.h b/api/task_queue/task_queue_test.h index e2e473017f..214f95008f 100644 --- a/api/task_queue/task_queue_test.h +++ b/api/task_queue/task_queue_test.h @@ -13,6 +13,7 @@ #include #include +#include "api/field_trials_view.h" #include "api/task_queue/task_queue_factory.h" #include "test/gtest.h" @@ -31,9 +32,9 @@ namespace webrtc { // INSTANTIATE_TEST_SUITE_P(My, TaskQueueTest, Values(CreateMyFactory)); // // } // namespace -class TaskQueueTest : public ::testing::TestWithParam< - std::function()>> { -}; +class TaskQueueTest + : public ::testing::TestWithParam(const FieldTrialsView*)>> {}; } // namespace webrtc diff --git a/api/task_queue/test/BUILD.gn b/api/task_queue/test/BUILD.gn index fa82dd090e..25f7ed0c7f 100644 --- a/api/task_queue/test/BUILD.gn +++ b/api/task_queue/test/BUILD.gn @@ -13,6 +13,8 @@ rtc_library("mock_task_queue_base") { sources = [ "mock_task_queue_base.h" ] deps = [ "../../../api/task_queue:task_queue", + "../../../api/units:time_delta", "../../../test:test_support", ] + absl_deps = [ "//third_party/abseil-cpp/absl/functional:any_invocable" ] } diff --git a/api/task_queue/test/mock_task_queue_base.h b/api/task_queue/test/mock_task_queue_base.h index 68c5c052ed..2e99be7e82 100644 --- a/api/task_queue/test/mock_task_queue_base.h +++ b/api/task_queue/test/mock_task_queue_base.h @@ -11,20 +11,25 @@ #ifndef API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ #define API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ -#include - +#include "absl/functional/any_invocable.h" #include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" #include "test/gmock.h" namespace webrtc { class MockTaskQueueBase : public TaskQueueBase { public: - MOCK_METHOD0(Delete, void()); - MOCK_METHOD1(PostTask, void(std::unique_ptr)); - MOCK_METHOD2(PostDelayedTask, void(std::unique_ptr, uint32_t)); - MOCK_METHOD2(PostDelayedHighPrecisionTask, - void(std::unique_ptr, uint32_t)); + MOCK_METHOD(void, Delete, (), (override)); + MOCK_METHOD(void, PostTask, (absl::AnyInvocable), (override)); + MOCK_METHOD(void, + PostDelayedTask, + (absl::AnyInvocable, TimeDelta), + (override)); + MOCK_METHOD(void, + PostDelayedHighPrecisionTask, + (absl::AnyInvocable, TimeDelta), + (override)); }; } // namespace webrtc diff --git a/api/test/DEPS b/api/test/DEPS index 48889d528f..270b274c5f 100644 --- a/api/test/DEPS +++ b/api/test/DEPS @@ -30,7 +30,6 @@ specific_include_rules = { "+modules/audio_processing/include/audio_processing.h", ], "time_controller\.h": [ - "+modules/utility/include/process_thread.h", "+rtc_base/synchronization/yield_policy.h", "+system_wrappers/include/clock.h", ], diff --git a/api/test/compile_all_headers.cc b/api/test/compile_all_headers.cc index b80c565843..1fcf63e97b 100644 --- a/api/test/compile_all_headers.cc +++ b/api/test/compile_all_headers.cc @@ -27,7 +27,6 @@ // "api/test/videocodec_test_fixture.h" // "api/test/videocodec_test_stats.h" -#include "api/test/dummy_peer_connection.h" #include "api/test/fake_frame_decryptor.h" #include "api/test/fake_frame_encryptor.h" #include "api/test/mock_async_dns_resolver.h" @@ -43,6 +42,7 @@ #include "api/test/mock_rtp_transceiver.h" #include "api/test/mock_rtpreceiver.h" #include "api/test/mock_rtpsender.h" +#include "api/test/mock_session_description_interface.h" #include "api/test/mock_transformable_video_frame.h" #include "api/test/mock_video_bitrate_allocator.h" #include "api/test/mock_video_bitrate_allocator_factory.h" diff --git a/api/test/create_frame_generator.cc b/api/test/create_frame_generator.cc index 7ed06473a1..5e6fb3228b 100644 --- a/api/test/create_frame_generator.cc +++ b/api/test/create_frame_generator.cc @@ -47,6 +47,23 @@ std::unique_ptr CreateFromYuvFileFrameGenerator( frame_repeat_count); } +std::unique_ptr CreateFromNV12FileFrameGenerator( + std::vector filenames, + size_t width, + size_t height, + int frame_repeat_count) { + RTC_DCHECK(!filenames.empty()); + std::vector files; + for (const std::string& filename : filenames) { + FILE* file = fopen(filename.c_str(), "rb"); + RTC_DCHECK(file != nullptr) << "Failed to open: '" << filename << "'\n"; + files.push_back(file); + } + + return std::make_unique(files, width, height, + frame_repeat_count); +} + std::unique_ptr CreateFromIvfFileFrameGenerator( std::string filename) { return std::make_unique(std::move(filename)); diff --git a/api/test/create_frame_generator.h b/api/test/create_frame_generator.h index cd4fcccd69..70be0c4e8e 100644 --- a/api/test/create_frame_generator.h +++ b/api/test/create_frame_generator.h @@ -41,6 +41,15 @@ std::unique_ptr CreateFromYuvFileFrameGenerator( size_t height, int frame_repeat_count); +// Creates a frame generator that repeatedly plays a set of nv12 files. +// The frame_repeat_count determines how many times each frame is shown, +// with 1 = show each frame once, etc. +std::unique_ptr CreateFromNV12FileFrameGenerator( + std::vector filenames, + size_t width, + size_t height, + int frame_repeat_count = 1); + // Creates a frame generator that repeatedly plays an ivf file. std::unique_ptr CreateFromIvfFileFrameGenerator( std::string filename); diff --git a/api/test/create_network_emulation_manager.cc b/api/test/create_network_emulation_manager.cc index 089a2f8a86..f5d5a1bc88 100644 --- a/api/test/create_network_emulation_manager.cc +++ b/api/test/create_network_emulation_manager.cc @@ -18,8 +18,10 @@ namespace webrtc { std::unique_ptr CreateNetworkEmulationManager( - TimeMode mode) { - return std::make_unique(mode); + TimeMode time_mode, + EmulatedNetworkStatsGatheringMode stats_gathering_mode) { + return std::make_unique( + time_mode, stats_gathering_mode); } } // namespace webrtc diff --git a/api/test/create_network_emulation_manager.h b/api/test/create_network_emulation_manager.h index f444743786..941b2b1c52 100644 --- a/api/test/create_network_emulation_manager.h +++ b/api/test/create_network_emulation_manager.h @@ -19,7 +19,9 @@ namespace webrtc { // Returns a non-null NetworkEmulationManager instance. std::unique_ptr CreateNetworkEmulationManager( - TimeMode mode = TimeMode::kRealTime); + TimeMode time_mode = TimeMode::kRealTime, + EmulatedNetworkStatsGatheringMode stats_gathering_mode = + EmulatedNetworkStatsGatheringMode::kDefault); } // namespace webrtc diff --git a/api/test/create_peer_connection_quality_test_frame_generator.cc b/api/test/create_peer_connection_quality_test_frame_generator.cc index 29eb41ca42..a1c53635f9 100644 --- a/api/test/create_peer_connection_quality_test_frame_generator.cc +++ b/api/test/create_peer_connection_quality_test_frame_generator.cc @@ -14,18 +14,13 @@ #include #include "api/test/create_frame_generator.h" -#include "api/test/peerconnection_quality_test_fixture.h" +#include "api/test/pclf/media_configuration.h" #include "rtc_base/checks.h" #include "test/testsupport/file_utils.h" namespace webrtc { namespace webrtc_pc_e2e { -using VideoConfig = - ::webrtc::webrtc_pc_e2e::PeerConnectionE2EQualityTestFixture::VideoConfig; -using ScreenShareConfig = ::webrtc::webrtc_pc_e2e:: - PeerConnectionE2EQualityTestFixture::ScreenShareConfig; - void ValidateScreenShareConfig(const VideoConfig& video_config, const ScreenShareConfig& screen_share_config) { if (screen_share_config.slides_yuv_file_names.empty()) { diff --git a/api/test/create_peer_connection_quality_test_frame_generator.h b/api/test/create_peer_connection_quality_test_frame_generator.h index ab3f65aa57..62043d140a 100644 --- a/api/test/create_peer_connection_quality_test_frame_generator.h +++ b/api/test/create_peer_connection_quality_test_frame_generator.h @@ -15,7 +15,7 @@ #include "absl/types/optional.h" #include "api/test/frame_generator_interface.h" -#include "api/test/peerconnection_quality_test_fixture.h" +#include "api/test/pclf/media_configuration.h" namespace webrtc { namespace webrtc_pc_e2e { @@ -25,19 +25,18 @@ namespace webrtc_pc_e2e { // FrameGeneratorInterface::OutputType::I420. video_config specifies frame // weight and height. std::unique_ptr CreateSquareFrameGenerator( - const PeerConnectionE2EQualityTestFixture::VideoConfig& video_config, + const VideoConfig& video_config, absl::optional type); // Creates a frame generator that plays frames from the yuv file. std::unique_ptr CreateFromYuvFileFrameGenerator( - const PeerConnectionE2EQualityTestFixture::VideoConfig& video_config, + const VideoConfig& video_config, std::string filename); // Creates a proper frame generator for testing screen sharing. std::unique_ptr CreateScreenShareFrameGenerator( - const PeerConnectionE2EQualityTestFixture::VideoConfig& video_config, - const PeerConnectionE2EQualityTestFixture::ScreenShareConfig& - screen_share_config); + const VideoConfig& video_config, + const ScreenShareConfig& screen_share_config); } // namespace webrtc_pc_e2e } // namespace webrtc diff --git a/api/test/create_peerconnection_quality_test_fixture.cc b/api/test/create_peerconnection_quality_test_fixture.cc index 2d9d0821fc..e156991ed4 100644 --- a/api/test/create_peerconnection_quality_test_fixture.cc +++ b/api/test/create_peerconnection_quality_test_fixture.cc @@ -13,6 +13,7 @@ #include #include +#include "api/test/metrics/global_metrics_logger_and_exporter.h" #include "api/test/time_controller.h" #include "test/pc/e2e/peer_connection_quality_test.h" @@ -27,7 +28,8 @@ CreatePeerConnectionE2EQualityTestFixture( std::unique_ptr video_quality_analyzer) { return std::make_unique( std::move(test_case_name), time_controller, - std::move(audio_quality_analyzer), std::move(video_quality_analyzer)); + std::move(audio_quality_analyzer), std::move(video_quality_analyzer), + test::GetGlobalMetricsLogger()); } } // namespace webrtc_pc_e2e diff --git a/api/test/create_time_controller.cc b/api/test/create_time_controller.cc index f7faeaab42..d198f2b0fe 100644 --- a/api/test/create_time_controller.cc +++ b/api/test/create_time_controller.cc @@ -37,23 +37,15 @@ std::unique_ptr CreateTimeControllerBasedCallFactory( explicit TimeControllerBasedCallFactory(TimeController* time_controller) : time_controller_(time_controller) {} Call* CreateCall(const Call::Config& config) override { - if (!module_thread_) { - module_thread_ = SharedModuleThread::Create( - time_controller_->CreateProcessThread("CallModules"), - [this]() { module_thread_ = nullptr; }); - } - RtpTransportConfig transportConfig = config.ExtractTransportConfig(); - return Call::Create(config, time_controller_->GetClock(), module_thread_, + return Call::Create(config, time_controller_->GetClock(), config.rtp_transport_controller_send_factory->Create( - transportConfig, time_controller_->GetClock(), - time_controller_->CreateProcessThread("Pacer"))); + transportConfig, time_controller_->GetClock())); } private: TimeController* time_controller_; - rtc::scoped_refptr module_thread_; }; return std::make_unique(time_controller); } diff --git a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h b/api/test/create_video_codec_tester.cc similarity index 50% rename from modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h rename to api/test/create_video_codec_tester.cc index d27ac893c6..a1efefdb48 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_encoder_supported.h +++ b/api/test/create_video_codec_tester.cc @@ -7,21 +7,21 @@ * 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_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ -#define MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ + +#include "api/test/create_video_codec_tester.h" #include +#include -#include "absl/base/attributes.h" -#include "absl/strings/string_view.h" -#include "api/video_codecs/video_encoder.h" +#include "api/test/video_codec_tester.h" +#include "modules/video_coding/codecs/test/video_codec_tester_impl.h" namespace webrtc { +namespace test { -ABSL_CONST_INIT extern const bool kIsLibaomAv1EncoderSupported; - -std::unique_ptr CreateLibaomAv1EncoderIfSupported(); +std::unique_ptr CreateVideoCodecTester() { + return std::make_unique(); +} +} // namespace test } // namespace webrtc - -#endif // MODULES_VIDEO_CODING_CODECS_AV1_LIBAOM_AV1_ENCODER_SUPPORTED_H_ diff --git a/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc b/api/test/create_video_codec_tester.h similarity index 54% rename from modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc rename to api/test/create_video_codec_tester.h index b97b68b33f..c68864ce85 100644 --- a/modules/video_coding/codecs/av1/libaom_av1_decoder_absent.cc +++ b/api/test/create_video_codec_tester.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 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 @@ -7,18 +7,20 @@ * 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/av1/libaom_av1_decoder.h" + +#ifndef API_TEST_CREATE_VIDEO_CODEC_TESTER_H_ +#define API_TEST_CREATE_VIDEO_CODEC_TESTER_H_ #include -#include "api/video_codecs/video_decoder.h" +#include "api/test/video_codec_tester.h" namespace webrtc { +namespace test { -const bool kIsLibaomAv1DecoderSupported = false; - -std::unique_ptr CreateLibaomAv1Decoder() { - return nullptr; -} +std::unique_ptr CreateVideoCodecTester(); +} // namespace test } // namespace webrtc + +#endif // API_TEST_CREATE_VIDEO_CODEC_TESTER_H_ diff --git a/api/test/dummy_peer_connection.h b/api/test/dummy_peer_connection.h deleted file mode 100644 index 4a262564a6..0000000000 --- a/api/test/dummy_peer_connection.h +++ /dev/null @@ -1,249 +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. - */ - -#ifndef API_TEST_DUMMY_PEER_CONNECTION_H_ -#define API_TEST_DUMMY_PEER_CONNECTION_H_ - -#include -#include -#include - -#include "api/peer_connection_interface.h" -#include "api/rtc_error.h" -#include "rtc_base/checks.h" -#include "rtc_base/ref_counted_object.h" - -namespace webrtc { - -// This class includes dummy implementations of all methods on the -// PeerconnectionInterface. Accessor/getter methods return empty or default -// values. State-changing methods with a return value return failure. Remaining -// methods (except Close())) will crash with FATAL if called. -class DummyPeerConnection : public PeerConnectionInterface { - rtc::scoped_refptr local_streams() override { - return nullptr; - } - rtc::scoped_refptr remote_streams() override { - return nullptr; - } - - bool AddStream(MediaStreamInterface* stream) override { return false; } - void RemoveStream(MediaStreamInterface* stream) override { - RTC_CHECK_NOTREACHED(); - } - - RTCErrorOr> AddTrack( - rtc::scoped_refptr track, - const std::vector& stream_ids) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - RTCError RemoveTrackOrError( - rtc::scoped_refptr sender) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - RTCErrorOr> AddTransceiver( - rtc::scoped_refptr track) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - RTCErrorOr> AddTransceiver( - rtc::scoped_refptr track, - const RtpTransceiverInit& init) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - RTCErrorOr> AddTransceiver( - cricket::MediaType media_type) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - RTCErrorOr> AddTransceiver( - cricket::MediaType media_type, - const RtpTransceiverInit& init) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - rtc::scoped_refptr CreateSender( - const std::string& kind, - const std::string& stream_id) override { - return nullptr; - } - - std::vector> GetSenders() - const override { - return {}; - } - - std::vector> GetReceivers() - const override { - return {}; - } - - std::vector> GetTransceivers() - const override { - return {}; - } - - bool GetStats(StatsObserver* observer, - MediaStreamTrackInterface* track, // Optional - StatsOutputLevel level) override { - return false; - } - - void GetStats(RTCStatsCollectorCallback* callback) override { - RTC_CHECK_NOTREACHED(); - } - void GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback) override { - RTC_CHECK_NOTREACHED(); - } - void GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback) override { - RTC_CHECK_NOTREACHED(); - } - void ClearStatsCache() override {} - - RTCErrorOr> CreateDataChannelOrError( - const std::string& label, - const DataChannelInit* config) override { - return RTCError(RTCErrorType::INTERNAL_ERROR, "Dummy function called"); - } - - const SessionDescriptionInterface* local_description() const override { - return nullptr; - } - const SessionDescriptionInterface* remote_description() const override { - return nullptr; - } - - const SessionDescriptionInterface* current_local_description() - const override { - return nullptr; - } - const SessionDescriptionInterface* current_remote_description() - const override { - return nullptr; - } - - const SessionDescriptionInterface* pending_local_description() - const override { - return nullptr; - } - const SessionDescriptionInterface* pending_remote_description() - const override { - return nullptr; - } - - void RestartIce() override { RTC_CHECK_NOTREACHED(); } - - // Create a new offer. - // The CreateSessionDescriptionObserver callback will be called when done. - void CreateOffer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) override { - RTC_CHECK_NOTREACHED(); - } - - void CreateAnswer(CreateSessionDescriptionObserver* observer, - const RTCOfferAnswerOptions& options) override { - RTC_CHECK_NOTREACHED(); - } - - void SetLocalDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) override { - RTC_CHECK_NOTREACHED(); - } - void SetRemoteDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) override { - RTC_CHECK_NOTREACHED(); - } - void SetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) - override { - RTC_CHECK_NOTREACHED(); - } - - PeerConnectionInterface::RTCConfiguration GetConfiguration() override { - return RTCConfiguration(); - } - RTCError SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& config) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - bool AddIceCandidate(const IceCandidateInterface* candidate) override { - return false; - } - bool RemoveIceCandidates( - const std::vector& candidates) override { - return false; - } - - RTCError SetBitrate(const BitrateSettings& bitrate) override { - return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Not implemented"); - } - - void SetAudioPlayout(bool playout) override { RTC_CHECK_NOTREACHED(); } - void SetAudioRecording(bool recording) override { RTC_CHECK_NOTREACHED(); } - - rtc::scoped_refptr LookupDtlsTransportByMid( - const std::string& mid) override { - return nullptr; - } - rtc::scoped_refptr GetSctpTransport() const override { - return nullptr; - } - - SignalingState signaling_state() override { return SignalingState(); } - - IceConnectionState ice_connection_state() override { - return IceConnectionState(); - } - - IceConnectionState standardized_ice_connection_state() override { - return IceConnectionState(); - } - - PeerConnectionState peer_connection_state() override { - return PeerConnectionState(); - } - - IceGatheringState ice_gathering_state() override { - return IceGatheringState(); - } - - absl::optional can_trickle_ice_candidates() { return absl::nullopt; } - - bool StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms) override { - return false; - } - bool StartRtcEventLog(std::unique_ptr output) override { - return false; - } - - void StopRtcEventLog() { RTC_CHECK_NOTREACHED(); } - - void Close() override {} - - rtc::Thread* signaling_thread() const override { - return rtc::Thread::Current(); - } -}; - -static_assert( - !std::is_abstract>::value, - ""); - -} // namespace webrtc - -#endif // API_TEST_DUMMY_PEER_CONNECTION_H_ diff --git a/api/test/fake_frame_decryptor.h b/api/test/fake_frame_decryptor.h index cb58dd6c99..783bc805c4 100644 --- a/api/test/fake_frame_decryptor.h +++ b/api/test/fake_frame_decryptor.h @@ -19,7 +19,6 @@ #include "api/array_view.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/media_types.h" -#include "rtc_base/ref_counted_object.h" namespace webrtc { diff --git a/api/test/frame_generator_interface.h b/api/test/frame_generator_interface.h index 90e60debac..d82ba55bdd 100644 --- a/api/test/frame_generator_interface.h +++ b/api/test/frame_generator_interface.h @@ -23,6 +23,10 @@ namespace test { class FrameGeneratorInterface { public: + struct Resolution { + size_t width; + size_t height; + }; struct VideoFrameData { VideoFrameData(rtc::scoped_refptr buffer, absl::optional update_rect) @@ -43,6 +47,8 @@ class FrameGeneratorInterface { // Change the capture resolution. virtual void ChangeResolution(size_t width, size_t height) = 0; + + virtual Resolution GetResolution() const = 0; }; } // namespace test diff --git a/api/test/metrics/BUILD.gn b/api/test/metrics/BUILD.gn new file mode 100644 index 0000000000..309b699329 --- /dev/null +++ b/api/test/metrics/BUILD.gn @@ -0,0 +1,281 @@ +# 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("../../../webrtc.gni") +if (rtc_enable_protobuf) { + import("//third_party/protobuf/proto_library.gni") +} + +group("metrics") { + deps = [ + ":global_metrics_logger_and_exporter", + ":metric", + ":metrics_accumulator", + ":metrics_exporter", + ":metrics_logger", + ":stdout_metrics_exporter", + ] +} + +if (rtc_include_tests) { + group("metrics_unittests") { + testonly = true + + deps = [ + ":global_metrics_logger_and_exporter_test", + ":metrics_accumulator_test", + ":metrics_logger_test", + ":print_result_proxy_metrics_exporter_test", + ":stdout_metrics_exporter_test", + ] + + if (rtc_enable_protobuf) { + deps += [ + ":chrome_perf_dashboard_metrics_exporter_test", + ":metrics_set_proto_file_exporter_test", + ] + } + } +} + +rtc_library("metric") { + visibility = [ "*" ] + sources = [ + "metric.cc", + "metric.h", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + deps = [ "../../../api/units:timestamp" ] +} + +rtc_library("metrics_logger") { + visibility = [ "*" ] + sources = [ + "metrics_logger.cc", + "metrics_logger.h", + ] + deps = [ + ":metric", + ":metrics_accumulator", + "../..:array_view", + "../../../rtc_base/synchronization:mutex", + "../../../system_wrappers", + "../../numerics", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("metrics_accumulator") { + visibility = [ "*" ] + sources = [ + "metrics_accumulator.cc", + "metrics_accumulator.h", + ] + deps = [ + ":metric", + "../../../rtc_base:macromagic", + "../../../rtc_base/synchronization:mutex", + "../../numerics", + "../../units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("metrics_exporter") { + visibility = [ "*" ] + sources = [ "metrics_exporter.h" ] + deps = [ + ":metric", + "../..:array_view", + ] +} + +rtc_library("stdout_metrics_exporter") { + visibility = [ "*" ] + sources = [ + "stdout_metrics_exporter.cc", + "stdout_metrics_exporter.h", + ] + deps = [ + ":metric", + ":metrics_exporter", + "../..:array_view", + "../../../rtc_base:stringutils", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] +} + +rtc_library("chrome_perf_dashboard_metrics_exporter") { + visibility = [ "*" ] + testonly = true + sources = [ + "chrome_perf_dashboard_metrics_exporter.cc", + "chrome_perf_dashboard_metrics_exporter.h", + ] + deps = [ + ":metric", + ":metrics_exporter", + "../../../api:array_view", + "../../../test:fileutils", + "../../../test:perf_test", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + ] +} + +if (rtc_enable_protobuf) { + proto_library("metric_proto") { + visibility = [ "*" ] + sources = [ "proto/metric.proto" ] + proto_out_dir = "api/test/metrics/proto" + cc_generator_options = "lite" + } +} + +rtc_library("metrics_set_proto_file_exporter") { + visibility = [ "*" ] + testonly = true + sources = [ + "metrics_set_proto_file_exporter.cc", + "metrics_set_proto_file_exporter.h", + ] + deps = [ + ":metric", + ":metrics_exporter", + "../..:array_view", + "../../../rtc_base:logging", + "../../../test:fileutils", + ] + + if (rtc_enable_protobuf) { + deps += [ ":metric_proto" ] + } +} + +rtc_library("print_result_proxy_metrics_exporter") { + visibility = [ "*" ] + testonly = true + sources = [ + "print_result_proxy_metrics_exporter.cc", + "print_result_proxy_metrics_exporter.h", + ] + deps = [ + ":metric", + ":metrics_exporter", + "../..:array_view", + "../../../test:perf_test", + ] +} + +rtc_library("global_metrics_logger_and_exporter") { + visibility = [ "*" ] + sources = [ + "global_metrics_logger_and_exporter.cc", + "global_metrics_logger_and_exporter.h", + ] + deps = [ + ":metrics_exporter", + ":metrics_logger", + "../../../rtc_base:checks", + "../../../system_wrappers", + ] +} + +if (rtc_include_tests) { + rtc_library("metrics_logger_test") { + testonly = true + sources = [ "metrics_logger_test.cc" ] + deps = [ + ":metric", + ":metrics_logger", + "../../../system_wrappers", + "../../../test:test_support", + "../../numerics", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + rtc_library("metrics_accumulator_test") { + testonly = true + sources = [ "metrics_accumulator_test.cc" ] + deps = [ + ":metric", + ":metrics_accumulator", + "../../../test:test_support", + "../../units:timestamp", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + rtc_library("stdout_metrics_exporter_test") { + testonly = true + sources = [ "stdout_metrics_exporter_test.cc" ] + deps = [ + ":metric", + ":stdout_metrics_exporter", + "../../../test:test_support", + "../../units:timestamp", + ] + } + + rtc_library("print_result_proxy_metrics_exporter_test") { + testonly = true + sources = [ "print_result_proxy_metrics_exporter_test.cc" ] + deps = [ + ":metric", + ":print_result_proxy_metrics_exporter", + "../../../test:test_support", + "../../units:timestamp", + ] + } + + rtc_library("global_metrics_logger_and_exporter_test") { + testonly = true + sources = [ "global_metrics_logger_and_exporter_test.cc" ] + deps = [ + ":global_metrics_logger_and_exporter", + ":metric", + ":metrics_exporter", + ":metrics_logger", + "../../../system_wrappers", + "../../../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] + } + + if (rtc_enable_protobuf) { + rtc_library("metrics_set_proto_file_exporter_test") { + testonly = true + sources = [ "metrics_set_proto_file_exporter_test.cc" ] + deps = [ + ":metric", + ":metric_proto", + ":metrics_set_proto_file_exporter", + "../../../rtc_base:protobuf_utils", + "../../../test:fileutils", + "../../../test:test_support", + "../../units:timestamp", + ] + } + + rtc_library("chrome_perf_dashboard_metrics_exporter_test") { + testonly = true + sources = [ "chrome_perf_dashboard_metrics_exporter_test.cc" ] + deps = [ + ":chrome_perf_dashboard_metrics_exporter", + ":metric", + "../../../api/units:timestamp", + "../../../test:fileutils", + "../../../test:test_support", + "//third_party/catapult/tracing/tracing:histogram", + ] + } + } +} diff --git a/api/test/metrics/DEPS b/api/test/metrics/DEPS new file mode 100644 index 0000000000..74889c61c7 --- /dev/null +++ b/api/test/metrics/DEPS @@ -0,0 +1,14 @@ +specific_include_rules = { + "metrics_logger_and_exporter\.h": [ + "+rtc_base/synchronization/mutex.h", + "+system_wrappers/include/clock.h", + ], + "metrics_logger\.h": [ + "+rtc_base/synchronization/mutex.h", + "+system_wrappers/include/clock.h", + ], + "metrics_accumulator\.h": [ + "+rtc_base/synchronization/mutex.h", + "+rtc_base/thread_annotations.h", + ], +} diff --git a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc new file mode 100644 index 0000000000..018d110b12 --- /dev/null +++ b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.cc @@ -0,0 +1,146 @@ +/* + * 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/metrics/chrome_perf_dashboard_metrics_exporter.h" + +#include + +#include +#include +#include + +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "test/testsupport/file_utils.h" +#include "test/testsupport/perf_test_histogram_writer.h" +#include "test/testsupport/perf_test_result_writer.h" + +namespace webrtc { +namespace test { +namespace { + +std::string ToChromePerfDashboardUnit(Unit unit) { + switch (unit) { + case Unit::kMilliseconds: + return "msBestFitFormat"; + case Unit::kPercent: + return "n%"; + case Unit::kBytes: + return "sizeInBytes"; + case Unit::kKilobitsPerSecond: + // Chrome Perf Dashboard doesn't have kpbs units, so we change the unit + // and value accordingly. + return "bytesPerSecond"; + case Unit::kHertz: + return "Hz"; + case Unit::kUnitless: + return "unitless"; + case Unit::kCount: + return "count"; + } +} + +double ToChromePerfDashboardValue(double value, Unit unit) { + switch (unit) { + case Unit::kKilobitsPerSecond: + // Chrome Perf Dashboard doesn't have kpbs units, so we change the unit + // and value accordingly. + return value * 1000 / 8; + default: + return value; + } +} + +ImproveDirection ToChromePerfDashboardImproveDirection( + ImprovementDirection direction) { + switch (direction) { + case ImprovementDirection::kBiggerIsBetter: + return ImproveDirection::kBiggerIsBetter; + case ImprovementDirection::kNeitherIsBetter: + return ImproveDirection::kNone; + case ImprovementDirection::kSmallerIsBetter: + return ImproveDirection::kSmallerIsBetter; + } +} + +bool WriteMetricsToFile(const std::string& path, const std::string& data) { + CreateDir(DirName(path)); + FILE* output = fopen(path.c_str(), "wb"); + if (output == NULL) { + printf("Failed to write to %s.\n", path.c_str()); + return false; + } + size_t written = fwrite(data.c_str(), sizeof(char), data.size(), output); + fclose(output); + + if (written != data.size()) { + size_t expected = data.size(); + printf("Wrote %zu, tried to write %zu\n", written, expected); + return false; + } + return true; +} + +bool IsEmpty(const Metric::Stats& stats) { + return !stats.mean.has_value() && !stats.stddev.has_value() && + !stats.min.has_value() && !stats.max.has_value(); +} + +} // namespace + +ChromePerfDashboardMetricsExporter::ChromePerfDashboardMetricsExporter( + absl::string_view export_file_path) + : export_file_path_(export_file_path) {} + +bool ChromePerfDashboardMetricsExporter::Export( + rtc::ArrayView metrics) { + std::unique_ptr writer = + absl::WrapUnique(CreateHistogramWriter()); + for (const Metric& metric : metrics) { + if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) { + // If there were no data collected for the metric it is expected that 0 + // will be exported, so add 0 to the samples. + writer->LogResult( + metric.name, metric.test_case, + ToChromePerfDashboardValue(0, metric.unit), + ToChromePerfDashboardUnit(metric.unit), + /*important=*/false, + ToChromePerfDashboardImproveDirection(metric.improvement_direction)); + continue; + } + + if (metric.time_series.samples.empty()) { + writer->LogResultMeanAndError( + metric.name, metric.test_case, + ToChromePerfDashboardValue(*metric.stats.mean, metric.unit), + ToChromePerfDashboardValue(*metric.stats.stddev, metric.unit), + ToChromePerfDashboardUnit(metric.unit), + /*important=*/false, + ToChromePerfDashboardImproveDirection(metric.improvement_direction)); + continue; + } + + std::vector samples(metric.time_series.samples.size()); + for (size_t i = 0; i < metric.time_series.samples.size(); ++i) { + samples[i] = ToChromePerfDashboardValue( + metric.time_series.samples[i].value, metric.unit); + } + writer->LogResultList( + metric.name, metric.test_case, samples, + ToChromePerfDashboardUnit(metric.unit), + /*important=*/false, + ToChromePerfDashboardImproveDirection(metric.improvement_direction)); + } + return WriteMetricsToFile(export_file_path_, writer->Serialize()); +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.h new file mode 100644 index 0000000000..dda17a08c6 --- /dev/null +++ b/api/test/metrics/chrome_perf_dashboard_metrics_exporter.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 API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_ +#define API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_ + +#include + +#include "absl/strings/string_view.h" +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_exporter.h" + +namespace webrtc { +namespace test { + +// Exports all collected metrics in the Chrome Perf Dashboard proto format. +class ChromePerfDashboardMetricsExporter : public MetricsExporter { + public: + // `export_file_path` - path where the proto file will be written. + explicit ChromePerfDashboardMetricsExporter( + absl::string_view export_file_path); + ~ChromePerfDashboardMetricsExporter() override = default; + + bool Export(rtc::ArrayView metrics) override; + + private: + const std::string export_file_path_; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_ diff --git a/api/test/metrics/chrome_perf_dashboard_metrics_exporter_test.cc b/api/test/metrics/chrome_perf_dashboard_metrics_exporter_test.cc new file mode 100644 index 0000000000..5d3136f49a --- /dev/null +++ b/api/test/metrics/chrome_perf_dashboard_metrics_exporter_test.cc @@ -0,0 +1,248 @@ +/* + * 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/metrics/chrome_perf_dashboard_metrics_exporter.h" + +#include +#include +#include + +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" +#include "third_party/catapult/tracing/tracing/value/histogram.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::DoubleNear; +using ::testing::Eq; +using ::testing::Test; + +namespace proto = ::catapult::tracing::tracing::proto; + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +Metric::TimeSeries::Sample Sample(double value) { + return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1), + .value = value, + .sample_metadata = DefaultMetadata()}; +} + +std::string ReadFileAsString(const std::string& filename) { + std::ifstream infile(filename, std::ios_base::binary); + auto buffer = std::vector(std::istreambuf_iterator(infile), + std::istreambuf_iterator()); + return std::string(buffer.begin(), buffer.end()); +} + +class ChromePerfDashboardMetricsExporterTest : public Test { + protected: + ~ChromePerfDashboardMetricsExporterTest() override = default; + + void SetUp() override { + temp_filename_ = webrtc::test::TempFilename( + webrtc::test::OutputPath(), + "chrome_perf_dashboard_metrics_exporter_test"); + } + + void TearDown() override { + ASSERT_TRUE(webrtc::test::RemoveFile(temp_filename_)); + } + + std::string temp_filename_; +}; + +TEST_F(ChromePerfDashboardMetricsExporterTest, ExportMetricFormatCorrect) { + Metric metric1{ + .name = "test_metric1", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name1", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}}, + .stats = + Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + Metric metric2{ + .name = "test_metric2", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kSmallerIsBetter, + .test_case = "test_case_name2", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}}, + .stats = Metric::Stats{ + .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}}; + + ChromePerfDashboardMetricsExporter exporter(temp_filename_); + + ASSERT_TRUE(exporter.Export(std::vector{metric1, metric2})); + proto::HistogramSet actual_histogram_set; + actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(2)); + + // Validate output for `metric1` + EXPECT_THAT(actual_histogram_set.histograms(0).name(), Eq("test_metric1")); + EXPECT_THAT(actual_histogram_set.histograms(0).unit().unit(), + Eq(proto::Unit::MS_BEST_FIT_FORMAT)); + EXPECT_THAT(actual_histogram_set.histograms(0).unit().improvement_direction(), + Eq(proto::ImprovementDirection::BIGGER_IS_BETTER)); + EXPECT_THAT( + actual_histogram_set.histograms(0).diagnostics().diagnostic_map().size(), + Eq(1lu)); + EXPECT_THAT(actual_histogram_set.histograms(0) + .diagnostics() + .diagnostic_map() + .at("stories") + .generic_set() + .values(0), + Eq("\"test_case_name1\"")); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(2)); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(10.0)); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(1), Eq(20.0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(2)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(20)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(), + DoubleNear(2.64916, 0.1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(15)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(10)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(30)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(50)); + + // Validate output for `metric2` + EXPECT_THAT(actual_histogram_set.histograms(1).name(), Eq("test_metric2")); + EXPECT_THAT(actual_histogram_set.histograms(1).unit().unit(), + Eq(proto::Unit::BYTES_PER_SECOND)); + EXPECT_THAT(actual_histogram_set.histograms(1).unit().improvement_direction(), + Eq(proto::ImprovementDirection::SMALLER_IS_BETTER)); + EXPECT_THAT( + actual_histogram_set.histograms(1).diagnostics().diagnostic_map().size(), + Eq(1lu)); + EXPECT_THAT(actual_histogram_set.histograms(1) + .diagnostics() + .diagnostic_map() + .at("stories") + .generic_set() + .values(0), + Eq("\"test_case_name2\"")); + EXPECT_THAT(actual_histogram_set.histograms(1).sample_values().size(), Eq(2)); + EXPECT_THAT(actual_histogram_set.histograms(1).sample_values(0), Eq(2500.0)); + EXPECT_THAT(actual_histogram_set.histograms(1).sample_values(1), Eq(5000.0)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().count(), Eq(2)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().max(), Eq(5000)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().meanlogs(), + DoubleNear(8.17062, 0.1)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().mean(), Eq(3750)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().min(), Eq(2500)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().sum(), Eq(7500)); + EXPECT_THAT(actual_histogram_set.histograms(1).running().variance(), + Eq(3125000)); +} + +TEST_F(ChromePerfDashboardMetricsExporterTest, + ExportEmptyMetricExportsZeroValue) { + Metric metric{.name = "test_metric", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{}}; + + ChromePerfDashboardMetricsExporter exporter(temp_filename_); + + ASSERT_TRUE(exporter.Export(std::vector{metric})); + proto::HistogramSet actual_histogram_set; + actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1)); + + // Validate values for `metric` + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(0.0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), + DoubleNear(0, 1e-6)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(), Eq(0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0)); +} + +TEST_F(ChromePerfDashboardMetricsExporterTest, + ExportMetricWithOnlyStatsExportsMeanValues) { + Metric metric{.name = "test_metric", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{ + .mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + + ChromePerfDashboardMetricsExporter exporter(temp_filename_); + + ASSERT_TRUE(exporter.Export(std::vector{metric})); + proto::HistogramSet actual_histogram_set; + actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1)); + + // Validate values for `metric` + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(15.0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(15)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(), + DoubleNear(2.70805, 0.1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(15)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(15)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(15)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0)); +} + +TEST_F(ChromePerfDashboardMetricsExporterTest, + ExportMetricWithOnlyStatsConvertsMeanValuesWhenRequired) { + Metric metric{.name = "test_metric", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{ + .mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + + ChromePerfDashboardMetricsExporter exporter(temp_filename_); + + ASSERT_TRUE(exporter.Export(std::vector{metric})); + proto::HistogramSet actual_histogram_set; + actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1)); + + // Validate values for `metric` + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(1875.0)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(1875)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(), + DoubleNear(7.53636, 0.1)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(1875)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(1875)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(1875)); + EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0)); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/global_metrics_logger_and_exporter.cc b/api/test/metrics/global_metrics_logger_and_exporter.cc new file mode 100644 index 0000000000..2d42a976aa --- /dev/null +++ b/api/test/metrics/global_metrics_logger_and_exporter.cc @@ -0,0 +1,42 @@ +/* + * 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/metrics/global_metrics_logger_and_exporter.h" + +#include +#include +#include + +#include "api/test/metrics/metrics_exporter.h" +#include "api/test/metrics/metrics_logger.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace test { + +DefaultMetricsLogger* GetGlobalMetricsLogger() { + static DefaultMetricsLogger* logger_ = + new DefaultMetricsLogger(Clock::GetRealTimeClock()); + return logger_; +} + +bool ExportPerfMetric(MetricsLogger& logger, + std::vector> exporters) { + std::vector metrics = logger.GetCollectedMetrics(); + bool success = true; + for (auto& exporter : exporters) { + bool export_result = exporter->Export(metrics); + success = success && export_result; + } + return success; +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/global_metrics_logger_and_exporter.h b/api/test/metrics/global_metrics_logger_and_exporter.h new file mode 100644 index 0000000000..f77ff1c737 --- /dev/null +++ b/api/test/metrics/global_metrics_logger_and_exporter.h @@ -0,0 +1,32 @@ +/* + * 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_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_ +#define API_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_ + +#include +#include + +#include "api/test/metrics/metrics_exporter.h" +#include "api/test/metrics/metrics_logger.h" + +namespace webrtc { +namespace test { + +// Returns non-null global `MetricsLogger` to log metrics. +DefaultMetricsLogger* GetGlobalMetricsLogger(); + +bool ExportPerfMetric(MetricsLogger& logger, + std::vector> exporters); + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_ diff --git a/api/test/metrics/global_metrics_logger_and_exporter_test.cc b/api/test/metrics/global_metrics_logger_and_exporter_test.cc new file mode 100644 index 0000000000..567b3da9e3 --- /dev/null +++ b/api/test/metrics/global_metrics_logger_and_exporter_test.cc @@ -0,0 +1,131 @@ +/* + * 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/metrics/global_metrics_logger_and_exporter.h" + +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_exporter.h" +#include "api/test/metrics/metrics_logger.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::Eq; +using ::testing::IsEmpty; + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +struct TestMetricsExporterFactory { + public: + std::unique_ptr CreateExporter() { + return std::make_unique(this, /*export_result=*/true); + } + + std::unique_ptr CreateFailureExporter() { + return std::make_unique(this, /*export_result=*/false); + } + + std::vector exported_metrics; + + private: + class TestMetricsExporter : public MetricsExporter { + public: + TestMetricsExporter(TestMetricsExporterFactory* factory, bool export_result) + : factory_(factory), export_result_(export_result) {} + ~TestMetricsExporter() override = default; + + bool Export(rtc::ArrayView metrics) override { + factory_->exported_metrics = + std::vector(metrics.begin(), metrics.end()); + return export_result_; + } + + TestMetricsExporterFactory* factory_; + bool export_result_; + }; +}; + +TEST(ExportPerfMetricTest, CollectedMetricsAreExporter) { + TestMetricsExporterFactory exporter_factory; + + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + logger.LogSingleValueMetric( + "metric_name", "test_case_name", + /*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + std::map{{"key", "value"}}); + + std::vector> exporters; + exporters.push_back(exporter_factory.CreateExporter()); + ASSERT_TRUE(ExportPerfMetric(logger, std::move(exporters))); + + std::vector metrics = exporter_factory.exported_metrics; + ASSERT_THAT(metrics.size(), Eq(1lu)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.time_series.samples.size(), Eq(1lu)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::nullopt); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +TEST(ExportPerfMetricTest, OneFailedExporterDoesNotPreventExportToOthers) { + TestMetricsExporterFactory exporter_factory1; + TestMetricsExporterFactory exporter_factory2; + TestMetricsExporterFactory exporter_factory3; + + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + logger.LogSingleValueMetric("metric_name", "test_case_name", + /*value=*/10, Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + + std::vector> exporters; + exporters.push_back(exporter_factory1.CreateExporter()); + exporters.push_back(exporter_factory2.CreateFailureExporter()); + exporters.push_back(exporter_factory3.CreateExporter()); + ASSERT_FALSE(ExportPerfMetric(logger, std::move(exporters))); + + std::vector metrics1 = exporter_factory1.exported_metrics; + std::vector metrics2 = exporter_factory2.exported_metrics; + std::vector metrics3 = exporter_factory3.exported_metrics; + ASSERT_THAT(metrics1.size(), Eq(1lu)) + << metrics1[0].name << "; " << metrics1[1].name; + EXPECT_THAT(metrics1[0].name, Eq("metric_name")); + ASSERT_THAT(metrics2.size(), Eq(1lu)); + EXPECT_THAT(metrics2[0].name, Eq("metric_name")); + ASSERT_THAT(metrics3.size(), Eq(1lu)); + EXPECT_THAT(metrics3[0].name, Eq("metric_name")); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metric.cc b/api/test/metrics/metric.cc new file mode 100644 index 0000000000..3c30f36f49 --- /dev/null +++ b/api/test/metrics/metric.cc @@ -0,0 +1,48 @@ +/* + * 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/metrics/metric.h" + +#include + +namespace webrtc { +namespace test { + +absl::string_view ToString(Unit unit) { + switch (unit) { + case Unit::kMilliseconds: + return "Milliseconds"; + case Unit::kPercent: + return "Percent"; + case Unit::kBytes: + return "Bytes"; + case Unit::kKilobitsPerSecond: + return "KilobitsPerSecond"; + case Unit::kHertz: + return "Hertz"; + case Unit::kUnitless: + return "Unitless"; + case Unit::kCount: + return "Count"; + } +} + +absl::string_view ToString(ImprovementDirection direction) { + switch (direction) { + case ImprovementDirection::kBiggerIsBetter: + return "BiggerIsBetter"; + case ImprovementDirection::kNeitherIsBetter: + return "NeitherIsBetter"; + case ImprovementDirection::kSmallerIsBetter: + return "SmallerIsBetter"; + } +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metric.h b/api/test/metrics/metric.h new file mode 100644 index 0000000000..17c1755f95 --- /dev/null +++ b/api/test/metrics/metric.h @@ -0,0 +1,96 @@ +/* + * 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_TEST_METRICS_METRIC_H_ +#define API_TEST_METRICS_METRIC_H_ + +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/units/timestamp.h" + +namespace webrtc { +namespace test { + +enum class Unit { + kMilliseconds, + kPercent, + kBytes, + kKilobitsPerSecond, + kHertz, + // General unitless value. Can be used either for dimensionless quantities + // (ex ratio) or for units not presented in this enum and too specific to add + // to this enum. + kUnitless, + kCount +}; + +absl::string_view ToString(Unit unit); + +enum class ImprovementDirection { + kBiggerIsBetter, + kNeitherIsBetter, + kSmallerIsBetter +}; + +absl::string_view ToString(ImprovementDirection direction); + +struct Metric { + struct TimeSeries { + struct Sample { + // Timestamp in microseconds associated with a sample. For example, + // the timestamp when the sample was collected. + webrtc::Timestamp timestamp; + double value; + // Metadata associated with this particular sample. + std::map sample_metadata; + }; + + // All samples collected for this metric. It can be empty if the Metric + // object only contains `stats`. + std::vector samples; + }; + + // Contains metric's precomputed statistics based on the `time_series` or if + // `time_series` is omitted (has 0 samples) contains precomputed statistics + // provided by the metric's calculator. + struct Stats { + // Sample mean of the metric + // (https://en.wikipedia.org/wiki/Sample_mean_and_covariance). + absl::optional mean; + // Standard deviation (https://en.wikipedia.org/wiki/Standard_deviation). + // Is undefined if `time_series` contains only a single value. + absl::optional stddev; + absl::optional min; + absl::optional max; + }; + + // Metric name, for example PSNR, SSIM, decode_time, etc. + std::string name; + Unit unit; + ImprovementDirection improvement_direction; + // If the metric is generated by a test, this field can be used to specify + // this information. + std::string test_case; + // Metadata associated with the whole metric. + std::map metric_metadata; + // Contains all samples of the metric collected during test execution. + // It can be empty if the user only stores precomputed statistics into + // `stats`. + TimeSeries time_series; + Stats stats; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_METRIC_H_ diff --git a/api/test/metrics/metrics_accumulator.cc b/api/test/metrics/metrics_accumulator.cc new file mode 100644 index 0000000000..c34396be97 --- /dev/null +++ b/api/test/metrics/metrics_accumulator.cc @@ -0,0 +1,132 @@ +/* + * 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/metrics/metrics_accumulator.h" + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { +namespace test { +namespace { + +Metric::Stats ToStats(const SamplesStatsCounter& values) { + if (values.IsEmpty()) { + return Metric::Stats(); + } + return Metric::Stats{.mean = values.GetAverage(), + .stddev = values.GetStandardDeviation(), + .min = values.GetMin(), + .max = values.GetMax()}; +} + +Metric SetTimeseries(const Metric& prototype, + const SamplesStatsCounter& counter) { + Metric output(prototype); + Metric::TimeSeries time_series; + for (const SamplesStatsCounter::StatsSample& sample : + counter.GetTimedSamples()) { + time_series.samples.push_back( + Metric::TimeSeries::Sample{.timestamp = sample.time, + .value = sample.value, + .sample_metadata = sample.metadata}); + } + output.time_series = std::move(time_series); + output.stats = ToStats(counter); + return output; +} + +} // namespace + +bool operator<(const MetricsAccumulator::MetricKey& a, + const MetricsAccumulator::MetricKey& b) { + if (a.test_case_name < b.test_case_name) { + return true; + } else if (a.test_case_name > b.test_case_name) { + return false; + } else { + return a.metric_name < b.metric_name; + } +} + +bool MetricsAccumulator::AddSample( + absl::string_view metric_name, + absl::string_view test_case_name, + double value, + Timestamp timestamp, + std::map point_metadata) { + MutexLock lock(&mutex_); + bool created; + MetricValue* metric_value = + GetOrCreateMetric(metric_name, test_case_name, &created); + metric_value->counter.AddSample( + SamplesStatsCounter::StatsSample{.value = value, + .time = timestamp, + .metadata = std::move(point_metadata)}); + return created; +} + +bool MetricsAccumulator::AddMetricMetadata( + absl::string_view metric_name, + absl::string_view test_case_name, + Unit unit, + ImprovementDirection improvement_direction, + std::map metric_metadata) { + MutexLock lock(&mutex_); + bool created; + MetricValue* metric_value = + GetOrCreateMetric(metric_name, test_case_name, &created); + metric_value->metric.unit = unit; + metric_value->metric.improvement_direction = improvement_direction; + metric_value->metric.metric_metadata = std::move(metric_metadata); + return created; +} + +std::vector MetricsAccumulator::GetCollectedMetrics() const { + MutexLock lock(&mutex_); + std::vector out; + out.reserve(metrics_.size()); + for (const auto& [unused_key, metric_value] : metrics_) { + out.push_back(SetTimeseries(metric_value.metric, metric_value.counter)); + } + return out; +} + +MetricsAccumulator::MetricValue* MetricsAccumulator::GetOrCreateMetric( + absl::string_view metric_name, + absl::string_view test_case_name, + bool* created) { + MetricKey key(metric_name, test_case_name); + auto it = metrics_.find(key); + if (it != metrics_.end()) { + *created = false; + return &it->second; + } + *created = true; + + Metric metric{ + .name = key.metric_name, + .unit = Unit::kUnitless, + .improvement_direction = ImprovementDirection::kNeitherIsBetter, + .test_case = key.test_case_name, + }; + return &metrics_.emplace(key, MetricValue{.metric = std::move(metric)}) + .first->second; +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metrics_accumulator.h b/api/test/metrics/metrics_accumulator.h new file mode 100644 index 0000000000..c75bd9429c --- /dev/null +++ b/api/test/metrics/metrics_accumulator.h @@ -0,0 +1,99 @@ +/* + * 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_TEST_METRICS_METRICS_ACCUMULATOR_H_ +#define API_TEST_METRICS_METRICS_ACCUMULATOR_H_ + +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { +namespace test { + +// Accumulates metrics' samples internally and provides API to get collected +// ones. +// +// This object is thread safe. +class MetricsAccumulator { + public: + MetricsAccumulator() = default; + + // Adds sample for the specified `metric_name` within specified + // `test_case_name`. If it is the first time when this combination of + // `metric_name` and `test_case_name` is used, creates a new Metric to collect + // samples, otherwise adds a sample to the previously created Metric. + // + // By default metric will use `Unit::kUnitless` and + // `ImprovementDirection::kNeitherIsBetter`. + // + // `point_metadata` - the metadata to be added to the single data point that + // this method adds to the Metric (it is not a metric global metadata). + // + // Returns true if a new metric was created and false otherwise. + bool AddSample(absl::string_view metric_name, + absl::string_view test_case_name, + double value, + Timestamp timestamp, + std::map point_metadata = {}); + + // Adds metadata to the metric specified by `metric_name` within specified + // `test_case_name`. If such a metric doesn't exist, creates a new one, + // otherwise overrides previously recorded values. + // + // Returns true if a new metric was created and false otherwise. + bool AddMetricMetadata( + absl::string_view metric_name, + absl::string_view test_case_name, + Unit unit, + ImprovementDirection improvement_direction, + std::map metric_metadata = {}); + + // Returns all metrics collected by this accumulator. No order guarantees + // provided. + std::vector GetCollectedMetrics() const; + + private: + struct MetricKey { + MetricKey(absl::string_view metric_name, absl::string_view test_case_name) + : metric_name(metric_name), test_case_name(test_case_name) {} + + std::string metric_name; + std::string test_case_name; + }; + friend bool operator<(const MetricKey& a, const MetricKey& b); + + struct MetricValue { + SamplesStatsCounter counter; + Metric metric; + }; + + // Gets existing metrics or creates a new one. If metric was created `created` + // will be set to true. + MetricValue* GetOrCreateMetric(absl::string_view metric_name, + absl::string_view test_case_name, + bool* created) + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + std::map metrics_ RTC_GUARDED_BY(mutex_); +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_METRICS_ACCUMULATOR_H_ diff --git a/api/test/metrics/metrics_accumulator_test.cc b/api/test/metrics/metrics_accumulator_test.cc new file mode 100644 index 0000000000..677f523339 --- /dev/null +++ b/api/test/metrics/metrics_accumulator_test.cc @@ -0,0 +1,315 @@ +/* + * 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/metrics/metrics_accumulator.h" + +#include +#include + +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +TEST(MetricsAccumulatorTest, AddSampleToTheNewMetricWillCreateOne) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddSample( + "metric_name", "test_case_name", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/std::map{{"key", "value"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metric.metric_metadata, IsEmpty()); + ASSERT_THAT(metric.time_series.samples, SizeIs(1)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +TEST(MetricsAccumulatorTest, AddSamplesToExistingMetricWontCreateNewOne) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddSample( + "metric_name", "test_case_name", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/ + std::map{{"key1", "value1"}})); + ASSERT_FALSE(accumulator.AddSample( + "metric_name", "test_case_name", + /*value=*/20, Timestamp::Seconds(2), + /*point_metadata=*/ + std::map{{"key2", "value2"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metric.metric_metadata, IsEmpty()); + ASSERT_THAT(metric.time_series.samples, SizeIs(2)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"key1", "value1"}})); + EXPECT_THAT(metric.time_series.samples[1].value, Eq(20.0)); + EXPECT_THAT(metric.time_series.samples[1].timestamp, + Eq(Timestamp::Seconds(2))); + EXPECT_THAT(metric.time_series.samples[1].sample_metadata, + Eq(std::map{{"key2", "value2"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(15.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(5.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(20.0)); +} + +TEST(MetricsAccumulatorTest, AddSampleToDifferentMetricsWillCreateBoth) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddSample( + "metric_name1", "test_case_name1", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/ + std::map{{"key1", "value1"}})); + ASSERT_TRUE(accumulator.AddSample( + "metric_name2", "test_case_name2", + /*value=*/20, Timestamp::Seconds(2), + /*point_metadata=*/ + std::map{{"key2", "value2"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[0].unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metrics[0].improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metrics[0].metric_metadata, IsEmpty()); + ASSERT_THAT(metrics[0].time_series.samples, SizeIs(1)); + EXPECT_THAT(metrics[0].time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metrics[0].time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metrics[0].time_series.samples[0].sample_metadata, + Eq(std::map{{"key1", "value1"}})); + ASSERT_THAT(metrics[0].stats.mean, absl::optional(10.0)); + ASSERT_THAT(metrics[0].stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metrics[0].stats.min, absl::optional(10.0)); + ASSERT_THAT(metrics[0].stats.max, absl::optional(10.0)); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); + EXPECT_THAT(metrics[1].unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metrics[1].improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metrics[1].metric_metadata, IsEmpty()); + ASSERT_THAT(metrics[1].time_series.samples, SizeIs(1)); + EXPECT_THAT(metrics[1].time_series.samples[0].value, Eq(20.0)); + EXPECT_THAT(metrics[1].time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(2))); + EXPECT_THAT(metrics[1].time_series.samples[0].sample_metadata, + Eq(std::map{{"key2", "value2"}})); + ASSERT_THAT(metrics[1].stats.mean, absl::optional(20.0)); + ASSERT_THAT(metrics[1].stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metrics[1].stats.min, absl::optional(20.0)); + ASSERT_THAT(metrics[1].stats.max, absl::optional(20.0)); +} + +TEST(MetricsAccumulatorTest, AddMetadataToTheNewMetricWillCreateOne) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddMetricMetadata( + "metric_name", "test_case_name", Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + /*metric_metadata=*/ + std::map{{"key", "value"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.time_series.samples, IsEmpty()); + ASSERT_THAT(metric.stats.mean, absl::nullopt); + ASSERT_THAT(metric.stats.stddev, absl::nullopt); + ASSERT_THAT(metric.stats.min, absl::nullopt); + ASSERT_THAT(metric.stats.max, absl::nullopt); +} + +TEST(MetricsAccumulatorTest, + AddMetadataToTheExistingMetricWillOverwriteValues) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddMetricMetadata( + "metric_name", "test_case_name", Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + /*metric_metadata=*/ + std::map{{"key1", "value1"}})); + + ASSERT_FALSE(accumulator.AddMetricMetadata( + "metric_name", "test_case_name", Unit::kBytes, + ImprovementDirection::kSmallerIsBetter, + /*metric_metadata=*/ + std::map{{"key2", "value2"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kBytes)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kSmallerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key2", "value2"}})); + ASSERT_THAT(metric.time_series.samples, IsEmpty()); + ASSERT_THAT(metric.stats.mean, absl::nullopt); + ASSERT_THAT(metric.stats.stddev, absl::nullopt); + ASSERT_THAT(metric.stats.min, absl::nullopt); + ASSERT_THAT(metric.stats.max, absl::nullopt); +} + +TEST(MetricsAccumulatorTest, AddMetadataToDifferentMetricsWillCreateBoth) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddMetricMetadata( + "metric_name1", "test_case_name1", Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + /*metric_metadata=*/ + std::map{{"key1", "value1"}})); + + ASSERT_TRUE(accumulator.AddMetricMetadata( + "metric_name2", "test_case_name2", Unit::kBytes, + ImprovementDirection::kSmallerIsBetter, + /*metric_metadata=*/ + std::map{{"key2", "value2"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[0].unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metrics[0].improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metrics[0].metric_metadata, + Eq(std::map{{"key1", "value1"}})); + ASSERT_THAT(metrics[0].time_series.samples, IsEmpty()); + ASSERT_THAT(metrics[0].stats.mean, absl::nullopt); + ASSERT_THAT(metrics[0].stats.stddev, absl::nullopt); + ASSERT_THAT(metrics[0].stats.min, absl::nullopt); + ASSERT_THAT(metrics[0].stats.max, absl::nullopt); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); + EXPECT_THAT(metrics[1].unit, Eq(Unit::kBytes)); + EXPECT_THAT(metrics[1].improvement_direction, + Eq(ImprovementDirection::kSmallerIsBetter)); + EXPECT_THAT(metrics[1].metric_metadata, + Eq(std::map{{"key2", "value2"}})); + ASSERT_THAT(metrics[1].time_series.samples, IsEmpty()); + ASSERT_THAT(metrics[1].stats.mean, absl::nullopt); + ASSERT_THAT(metrics[1].stats.stddev, absl::nullopt); + ASSERT_THAT(metrics[1].stats.min, absl::nullopt); + ASSERT_THAT(metrics[1].stats.max, absl::nullopt); +} + +TEST(MetricsAccumulatorTest, AddMetadataAfterAddingSampleWontCreateNewMetric) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddSample( + "metric_name", "test_case_name", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/ + std::map{{"key_s", "value_s"}})); + ASSERT_FALSE(accumulator.AddMetricMetadata( + "metric_name", "test_case_name", Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + /*metric_metadata=*/ + std::map{{"key_m", "value_m"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key_m", "value_m"}})); + ASSERT_THAT(metric.time_series.samples, SizeIs(1)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"key_s", "value_s"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +TEST(MetricsAccumulatorTest, AddSampleAfterAddingMetadataWontCreateNewMetric) { + MetricsAccumulator accumulator; + ASSERT_TRUE(accumulator.AddMetricMetadata( + "metric_name", "test_case_name", Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + /*metric_metadata=*/ + std::map{{"key_m", "value_m"}})); + ASSERT_FALSE(accumulator.AddSample( + "metric_name", "test_case_name", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/ + std::map{{"key_s", "value_s"}})); + + std::vector metrics = accumulator.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key_m", "value_m"}})); + ASSERT_THAT(metric.time_series.samples, SizeIs(1)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"key_s", "value_s"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metrics_exporter.h b/api/test/metrics/metrics_exporter.h new file mode 100644 index 0000000000..23954b6b1f --- /dev/null +++ b/api/test/metrics/metrics_exporter.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 API_TEST_METRICS_METRICS_EXPORTER_H_ +#define API_TEST_METRICS_METRICS_EXPORTER_H_ + +#include "api/array_view.h" +#include "api/test/metrics/metric.h" + +namespace webrtc { +namespace test { + +// Exports metrics in the requested format. +class MetricsExporter { + public: + virtual ~MetricsExporter() = default; + + // Exports specified metrics in a format that depends on the implementation. + // Returns true if export succeeded, false otherwise. + virtual bool Export(rtc::ArrayView metrics) = 0; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_METRICS_EXPORTER_H_ diff --git a/api/test/metrics/metrics_logger.cc b/api/test/metrics/metrics_logger.cc new file mode 100644 index 0000000000..1e24400367 --- /dev/null +++ b/api/test/metrics/metrics_logger.cc @@ -0,0 +1,114 @@ +/* + * 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/metrics/metrics_logger.h" + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { +namespace test { +namespace { + +Metric::Stats ToStats(const SamplesStatsCounter& values) { + if (values.IsEmpty()) { + return Metric::Stats(); + } + return Metric::Stats{.mean = values.GetAverage(), + .stddev = values.GetStandardDeviation(), + .min = values.GetMin(), + .max = values.GetMax()}; +} + +} // namespace + +void DefaultMetricsLogger::LogSingleValueMetric( + absl::string_view name, + absl::string_view test_case_name, + double value, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata) { + MutexLock lock(&mutex_); + metrics_.push_back(Metric{ + .name = std::string(name), + .unit = unit, + .improvement_direction = improvement_direction, + .test_case = std::string(test_case_name), + .metric_metadata = std::move(metadata), + .time_series = + Metric::TimeSeries{.samples = std::vector{Metric::TimeSeries::Sample{ + .timestamp = Now(), .value = value}}}, + .stats = Metric::Stats{ + .mean = value, .stddev = absl::nullopt, .min = value, .max = value}}); +} + +void DefaultMetricsLogger::LogMetric( + absl::string_view name, + absl::string_view test_case_name, + const SamplesStatsCounter& values, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata) { + MutexLock lock(&mutex_); + Metric::TimeSeries time_series; + for (const SamplesStatsCounter::StatsSample& sample : + values.GetTimedSamples()) { + time_series.samples.push_back( + Metric::TimeSeries::Sample{.timestamp = sample.time, + .value = sample.value, + .sample_metadata = sample.metadata}); + } + + metrics_.push_back(Metric{.name = std::string(name), + .unit = unit, + .improvement_direction = improvement_direction, + .test_case = std::string(test_case_name), + .metric_metadata = std::move(metadata), + .time_series = std::move(time_series), + .stats = ToStats(values)}); +} + +void DefaultMetricsLogger::LogMetric( + absl::string_view name, + absl::string_view test_case_name, + const Metric::Stats& metric_stats, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata) { + MutexLock lock(&mutex_); + metrics_.push_back(Metric{.name = std::string(name), + .unit = unit, + .improvement_direction = improvement_direction, + .test_case = std::string(test_case_name), + .metric_metadata = std::move(metadata), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = std::move(metric_stats)}); +} + +std::vector DefaultMetricsLogger::GetCollectedMetrics() const { + std::vector out = metrics_accumulator_.GetCollectedMetrics(); + MutexLock lock(&mutex_); + out.insert(out.end(), metrics_.begin(), metrics_.end()); + return out; +} + +Timestamp DefaultMetricsLogger::Now() { + return clock_->CurrentTime(); +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metrics_logger.h b/api/test/metrics/metrics_logger.h new file mode 100644 index 0000000000..66f9e55b95 --- /dev/null +++ b/api/test/metrics/metrics_logger.h @@ -0,0 +1,112 @@ +/* + * 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_TEST_METRICS_METRICS_LOGGER_H_ +#define API_TEST_METRICS_METRICS_LOGGER_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_view.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_accumulator.h" +#include "rtc_base/synchronization/mutex.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace test { + +// Provides API to log and collect performance metrics. +class MetricsLogger { + public: + virtual ~MetricsLogger() = default; + + // Adds a metric with a single value. + // `metadata` - metric's level metadata to add. + virtual void LogSingleValueMetric( + absl::string_view name, + absl::string_view test_case_name, + double value, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) = 0; + + // Adds metrics with a time series created based on the provided `values`. + // `metadata` - metric's level metadata to add. + virtual void LogMetric(absl::string_view name, + absl::string_view test_case_name, + const SamplesStatsCounter& values, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) = 0; + + // Adds metric with a time series with only stats object and without actual + // collected values. + // `metadata` - metric's level metadata to add. + virtual void LogMetric(absl::string_view name, + absl::string_view test_case_name, + const Metric::Stats& metric_stats, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) = 0; + + // Returns all metrics collected by this logger. + virtual std::vector GetCollectedMetrics() const = 0; +}; + +class DefaultMetricsLogger : public MetricsLogger { + public: + explicit DefaultMetricsLogger(webrtc::Clock* clock) : clock_(clock) {} + ~DefaultMetricsLogger() override = default; + + void LogSingleValueMetric( + absl::string_view name, + absl::string_view test_case_name, + double value, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) override; + + void LogMetric(absl::string_view name, + absl::string_view test_case_name, + const SamplesStatsCounter& values, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) override; + + void LogMetric(absl::string_view name, + absl::string_view test_case_name, + const Metric::Stats& metric_stats, + Unit unit, + ImprovementDirection improvement_direction, + std::map metadata = {}) override; + + // Returns all metrics collected by this logger and its `MetricsAccumulator`. + std::vector GetCollectedMetrics() const override; + + MetricsAccumulator* GetMetricsAccumulator() { return &metrics_accumulator_; } + + private: + webrtc::Timestamp Now(); + + webrtc::Clock* const clock_; + MetricsAccumulator metrics_accumulator_; + + mutable Mutex mutex_; + std::vector metrics_ RTC_GUARDED_BY(mutex_); +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_METRICS_LOGGER_H_ diff --git a/api/test/metrics/metrics_logger_test.cc b/api/test/metrics/metrics_logger_test.cc new file mode 100644 index 0000000000..de4501ca36 --- /dev/null +++ b/api/test/metrics/metrics_logger_test.cc @@ -0,0 +1,326 @@ +/* + * 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/metrics/metrics_logger.h" + +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::Eq; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +TEST(DefaultMetricsLoggerTest, LogSingleValueMetricRecordsMetric) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + logger.LogSingleValueMetric( + "metric_name", "test_case_name", + /*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + std::map{{"key", "value"}}); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.time_series.samples, SizeIs(1)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::nullopt); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +TEST(DefaultMetricsLoggerTest, LogMetricWithSamplesStatsCounterRecordsMetric) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + + SamplesStatsCounter values; + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 10, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = + std::map{{"point_key1", "value1"}}}); + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 20, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = + std::map{{"point_key2", "value2"}}}); + logger.LogMetric("metric_name", "test_case_name", values, Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + std::map{{"key", "value"}}); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.time_series.samples, SizeIs(2)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"point_key1", "value1"}})); + EXPECT_THAT(metric.time_series.samples[1].value, Eq(20.0)); + EXPECT_THAT(metric.time_series.samples[1].sample_metadata, + Eq(std::map{{"point_key2", "value2"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(15.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(5.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(20.0)); +} + +TEST(DefaultMetricsLoggerTest, + LogMetricWithEmptySamplesStatsCounterRecordsEmptyMetric) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + SamplesStatsCounter values; + logger.LogMetric("metric_name", "test_case_name", values, Unit::kUnitless, + ImprovementDirection::kBiggerIsBetter, DefaultMetadata()); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + EXPECT_THAT(metrics[0].name, Eq("metric_name")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name")); + EXPECT_THAT(metrics[0].time_series.samples, IsEmpty()); + ASSERT_THAT(metrics[0].stats.mean, Eq(absl::nullopt)); + ASSERT_THAT(metrics[0].stats.stddev, Eq(absl::nullopt)); + ASSERT_THAT(metrics[0].stats.min, Eq(absl::nullopt)); + ASSERT_THAT(metrics[0].stats.max, Eq(absl::nullopt)); +} + +TEST(DefaultMetricsLoggerTest, LogMetricWithStatsRecordsMetric) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20}; + logger.LogMetric("metric_name", "test_case_name", metric_stats, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + std::map{{"key", "value"}}); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metric.metric_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.time_series.samples, IsEmpty()); + ASSERT_THAT(metric.stats.mean, absl::optional(15.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(5.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(20.0)); +} + +TEST(DefaultMetricsLoggerTest, LogSingleValueMetricRecordsMultipleMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + + logger.LogSingleValueMetric("metric_name1", "test_case_name1", + /*value=*/10, Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + logger.LogSingleValueMetric("metric_name2", "test_case_name2", + /*value=*/10, Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); +} + +TEST(DefaultMetricsLoggerTest, + LogMetricWithSamplesStatsCounterRecordsMultipleMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + SamplesStatsCounter values; + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 10, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = DefaultMetadata()}); + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 20, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = DefaultMetadata()}); + + logger.LogMetric("metric_name1", "test_case_name1", values, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + logger.LogMetric("metric_name2", "test_case_name2", values, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); +} + +TEST(DefaultMetricsLoggerTest, LogMetricWithStatsRecordsMultipleMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20}; + + logger.LogMetric("metric_name1", "test_case_name1", metric_stats, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + logger.LogMetric("metric_name2", "test_case_name2", metric_stats, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); +} + +TEST(DefaultMetricsLoggerTest, + LogMetricThroughtAllMethodsAccumulateAllMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + SamplesStatsCounter values; + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 10, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = DefaultMetadata()}); + values.AddSample(SamplesStatsCounter::StatsSample{ + .value = 20, + .time = Clock::GetRealTimeClock()->CurrentTime(), + .metadata = DefaultMetadata()}); + Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20}; + + logger.LogSingleValueMetric("metric_name1", "test_case_name1", + /*value=*/10, Unit::kMilliseconds, + ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + logger.LogMetric("metric_name2", "test_case_name2", values, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + logger.LogMetric("metric_name3", "test_case_name3", metric_stats, + Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + DefaultMetadata()); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics.size(), Eq(3lu)); + EXPECT_THAT(metrics[0].name, Eq("metric_name1")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[1].name, Eq("metric_name2")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2")); + EXPECT_THAT(metrics[2].name, Eq("metric_name3")); + EXPECT_THAT(metrics[2].test_case, Eq("test_case_name3")); +} + +TEST(DefaultMetricsLoggerTest, AccumulatedMetricsReturnedInCollectedMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + logger.GetMetricsAccumulator()->AddSample( + "metric_name", "test_case_name", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/std::map{{"key", "value"}}); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(1)); + const Metric& metric = metrics[0]; + EXPECT_THAT(metric.name, Eq("metric_name")); + EXPECT_THAT(metric.test_case, Eq("test_case_name")); + EXPECT_THAT(metric.unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metric.improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metric.metric_metadata, IsEmpty()); + ASSERT_THAT(metric.time_series.samples, SizeIs(1)); + EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metric.time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metric.time_series.samples[0].sample_metadata, + Eq(std::map{{"key", "value"}})); + ASSERT_THAT(metric.stats.mean, absl::optional(10.0)); + ASSERT_THAT(metric.stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metric.stats.min, absl::optional(10.0)); + ASSERT_THAT(metric.stats.max, absl::optional(10.0)); +} + +TEST(DefaultMetricsLoggerTest, + AccumulatedMetricsReturnedTogetherWithLoggedMetrics) { + DefaultMetricsLogger logger(Clock::GetRealTimeClock()); + logger.LogSingleValueMetric( + "metric_name1", "test_case_name1", + /*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter, + std::map{{"key_m", "value_m"}}); + logger.GetMetricsAccumulator()->AddSample( + "metric_name2", "test_case_name2", + /*value=*/10, Timestamp::Seconds(1), + /*point_metadata=*/ + std::map{{"key_s", "value_s"}}); + + std::vector metrics = logger.GetCollectedMetrics(); + ASSERT_THAT(metrics, SizeIs(2)); + EXPECT_THAT(metrics[0].name, Eq("metric_name2")); + EXPECT_THAT(metrics[0].test_case, Eq("test_case_name2")); + EXPECT_THAT(metrics[0].unit, Eq(Unit::kUnitless)); + EXPECT_THAT(metrics[0].improvement_direction, + Eq(ImprovementDirection::kNeitherIsBetter)); + EXPECT_THAT(metrics[0].metric_metadata, IsEmpty()); + ASSERT_THAT(metrics[0].time_series.samples, SizeIs(1)); + EXPECT_THAT(metrics[0].time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metrics[0].time_series.samples[0].timestamp, + Eq(Timestamp::Seconds(1))); + EXPECT_THAT(metrics[0].time_series.samples[0].sample_metadata, + Eq(std::map{{"key_s", "value_s"}})); + ASSERT_THAT(metrics[0].stats.mean, absl::optional(10.0)); + ASSERT_THAT(metrics[0].stats.stddev, absl::optional(0.0)); + ASSERT_THAT(metrics[0].stats.min, absl::optional(10.0)); + ASSERT_THAT(metrics[0].stats.max, absl::optional(10.0)); + EXPECT_THAT(metrics[1].name, Eq("metric_name1")); + EXPECT_THAT(metrics[1].test_case, Eq("test_case_name1")); + EXPECT_THAT(metrics[1].unit, Eq(Unit::kMilliseconds)); + EXPECT_THAT(metrics[1].improvement_direction, + Eq(ImprovementDirection::kBiggerIsBetter)); + EXPECT_THAT(metrics[1].metric_metadata, + Eq(std::map{{"key_m", "value_m"}})); + ASSERT_THAT(metrics[1].time_series.samples, SizeIs(1)); + EXPECT_THAT(metrics[1].time_series.samples[0].value, Eq(10.0)); + EXPECT_THAT(metrics[1].time_series.samples[0].sample_metadata, + Eq(std::map{})); + ASSERT_THAT(metrics[1].stats.mean, absl::optional(10.0)); + ASSERT_THAT(metrics[1].stats.stddev, absl::nullopt); + ASSERT_THAT(metrics[1].stats.min, absl::optional(10.0)); + ASSERT_THAT(metrics[1].stats.max, absl::optional(10.0)); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metrics_set_proto_file_exporter.cc b/api/test/metrics/metrics_set_proto_file_exporter.cc new file mode 100644 index 0000000000..f6f3d392a2 --- /dev/null +++ b/api/test/metrics/metrics_set_proto_file_exporter.cc @@ -0,0 +1,166 @@ +/* + * 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/metrics/metrics_set_proto_file_exporter.h" + +#include + +#include +#include +#include + +#include "api/test/metrics/metric.h" +#include "rtc_base/logging.h" +#include "test/testsupport/file_utils.h" + +#if WEBRTC_ENABLE_PROTOBUF +#include "api/test/metrics/proto/metric.pb.h" +#endif + +namespace webrtc { +namespace test { +namespace { + +#if WEBRTC_ENABLE_PROTOBUF +webrtc::test_metrics::Unit ToProtoUnit(Unit unit) { + switch (unit) { + case Unit::kMilliseconds: + return webrtc::test_metrics::Unit::MILLISECONDS; + case Unit::kPercent: + return webrtc::test_metrics::Unit::PERCENT; + case Unit::kBytes: + return webrtc::test_metrics::Unit::BYTES; + case Unit::kKilobitsPerSecond: + return webrtc::test_metrics::Unit::KILOBITS_PER_SECOND; + case Unit::kHertz: + return webrtc::test_metrics::Unit::HERTZ; + case Unit::kUnitless: + return webrtc::test_metrics::Unit::UNITLESS; + case Unit::kCount: + return webrtc::test_metrics::Unit::COUNT; + } +} + +webrtc::test_metrics::ImprovementDirection ToProtoImprovementDirection( + ImprovementDirection direction) { + switch (direction) { + case ImprovementDirection::kBiggerIsBetter: + return webrtc::test_metrics::ImprovementDirection::BIGGER_IS_BETTER; + case ImprovementDirection::kNeitherIsBetter: + return webrtc::test_metrics::ImprovementDirection::NEITHER_IS_BETTER; + case ImprovementDirection::kSmallerIsBetter: + return webrtc::test_metrics::ImprovementDirection::SMALLER_IS_BETTER; + } +} + +void SetTimeSeries( + const Metric::TimeSeries& time_series, + webrtc::test_metrics::Metric::TimeSeries* proto_time_series) { + for (const Metric::TimeSeries::Sample& sample : time_series.samples) { + webrtc::test_metrics::Metric::TimeSeries::Sample* proto_sample = + proto_time_series->add_samples(); + proto_sample->set_value(sample.value); + proto_sample->set_timestamp_us(sample.timestamp.us()); + for (const auto& [key, value] : sample.sample_metadata) { + proto_sample->mutable_sample_metadata()->insert({key, value}); + } + } +} + +void SetStats(const Metric::Stats& stats, + webrtc::test_metrics::Metric::Stats* proto_stats) { + if (stats.mean.has_value()) { + proto_stats->set_mean(*stats.mean); + } + if (stats.stddev.has_value()) { + proto_stats->set_stddev(*stats.stddev); + } + if (stats.min.has_value()) { + proto_stats->set_min(*stats.min); + } + if (stats.max.has_value()) { + proto_stats->set_max(*stats.max); + } +} + +bool WriteMetricsToFile(const std::string& path, + const webrtc::test_metrics::MetricsSet& metrics_set) { + std::string data; + bool ok = metrics_set.SerializeToString(&data); + if (!ok) { + RTC_LOG(LS_ERROR) << "Failed to serialize histogram set to string"; + return false; + } + + CreateDir(DirName(path)); + FILE* output = fopen(path.c_str(), "wb"); + if (output == NULL) { + RTC_LOG(LS_ERROR) << "Failed to write to " << path; + return false; + } + size_t written = fwrite(data.c_str(), sizeof(char), data.size(), output); + fclose(output); + + if (written != data.size()) { + size_t expected = data.size(); + RTC_LOG(LS_ERROR) << "Wrote " << written << ", tried to write " << expected; + return false; + } + return true; +} +#endif // WEBRTC_ENABLE_PROTOBUF + +} // namespace + +MetricsSetProtoFileExporter::Options::Options( + absl::string_view export_file_path) + : export_file_path(export_file_path) {} +MetricsSetProtoFileExporter::Options::Options( + absl::string_view export_file_path, + bool export_whole_time_series) + : export_file_path(export_file_path), + export_whole_time_series(export_whole_time_series) {} +MetricsSetProtoFileExporter::Options::Options( + absl::string_view export_file_path, + std::map metadata) + : export_file_path(export_file_path), metadata(std::move(metadata)) {} + +bool MetricsSetProtoFileExporter::Export(rtc::ArrayView metrics) { +#if WEBRTC_ENABLE_PROTOBUF + webrtc::test_metrics::MetricsSet metrics_set; + for (const auto& [key, value] : options_.metadata) { + metrics_set.mutable_metadata()->insert({key, value}); + } + for (const Metric& metric : metrics) { + webrtc::test_metrics::Metric* metric_proto = metrics_set.add_metrics(); + metric_proto->set_name(metric.name); + metric_proto->set_unit(ToProtoUnit(metric.unit)); + metric_proto->set_improvement_direction( + ToProtoImprovementDirection(metric.improvement_direction)); + metric_proto->set_test_case(metric.test_case); + for (const auto& [key, value] : metric.metric_metadata) { + metric_proto->mutable_metric_metadata()->insert({key, value}); + } + + if (options_.export_whole_time_series) { + SetTimeSeries(metric.time_series, metric_proto->mutable_time_series()); + } + SetStats(metric.stats, metric_proto->mutable_stats()); + } + + return WriteMetricsToFile(options_.export_file_path, metrics_set); +#else + RTC_LOG(LS_ERROR) + << "Compile with protobuf support to properly use this class"; + return false; +#endif +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/metrics_set_proto_file_exporter.h b/api/test/metrics/metrics_set_proto_file_exporter.h new file mode 100644 index 0000000000..586ab83d00 --- /dev/null +++ b/api/test/metrics/metrics_set_proto_file_exporter.h @@ -0,0 +1,59 @@ +/* + * 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_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_ +#define API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_ + +#include +#include + +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_exporter.h" + +namespace webrtc { +namespace test { + +// Exports all collected metrics to the proto file using +// `webrtc::test_metrics::MetricsSet` format. +class MetricsSetProtoFileExporter : public MetricsExporter { + public: + struct Options { + explicit Options(absl::string_view export_file_path); + Options(absl::string_view export_file_path, bool export_whole_time_series); + Options(absl::string_view export_file_path, + std::map metadata); + + // File to export proto. + std::string export_file_path; + // If true will write all time series values to the output proto file, + // otherwise will write stats only. + bool export_whole_time_series = true; + // Metadata associated to the whole MetricsSet. + std::map metadata; + }; + + explicit MetricsSetProtoFileExporter(const Options& options) + : options_(options) {} + + MetricsSetProtoFileExporter(const MetricsSetProtoFileExporter&) = delete; + MetricsSetProtoFileExporter& operator=(const MetricsSetProtoFileExporter&) = + delete; + + bool Export(rtc::ArrayView metrics) override; + + private: + const Options options_; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_ diff --git a/api/test/metrics/metrics_set_proto_file_exporter_test.cc b/api/test/metrics/metrics_set_proto_file_exporter_test.cc new file mode 100644 index 0000000000..9202d31343 --- /dev/null +++ b/api/test/metrics/metrics_set_proto_file_exporter_test.cc @@ -0,0 +1,172 @@ +/* + * 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/metrics/metrics_set_proto_file_exporter.h" + +#include +#include +#include +#include + +#include "api/test/metrics/metric.h" +#include "api/test/metrics/proto/metric.pb.h" +#include "api/units/timestamp.h" +#include "rtc_base/protobuf_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::Eq; +using ::testing::Test; + +namespace proto = ::webrtc::test_metrics; + +std::string ReadFileAsString(const std::string& filename) { + std::ifstream infile(filename, std::ios_base::binary); + auto buffer = std::vector(std::istreambuf_iterator(infile), + std::istreambuf_iterator()); + return std::string(buffer.begin(), buffer.end()); +} + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +Metric::TimeSeries::Sample Sample(double value) { + return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1), + .value = value, + .sample_metadata = DefaultMetadata()}; +} + +void AssertSamplesEqual(const proto::Metric::TimeSeries::Sample& actual_sample, + const Metric::TimeSeries::Sample& expected_sample) { + EXPECT_THAT(actual_sample.value(), Eq(expected_sample.value)); + EXPECT_THAT(actual_sample.timestamp_us(), Eq(expected_sample.timestamp.us())); + EXPECT_THAT(actual_sample.sample_metadata().size(), + Eq(expected_sample.sample_metadata.size())); + for (const auto& [key, value] : expected_sample.sample_metadata) { + EXPECT_THAT(actual_sample.sample_metadata().at(key), Eq(value)); + } +} + +class MetricsSetProtoFileExporterTest : public Test { + protected: + ~MetricsSetProtoFileExporterTest() override = default; + + void SetUp() override { + temp_filename_ = webrtc::test::TempFilename( + webrtc::test::OutputPath(), "metrics_set_proto_file_exporter_test"); + } + + void TearDown() override { + ASSERT_TRUE(webrtc::test::RemoveFile(temp_filename_)); + } + + std::string temp_filename_; +}; + +TEST_F(MetricsSetProtoFileExporterTest, MetricsAreExportedCorrectly) { + MetricsSetProtoFileExporter::Options options(temp_filename_); + MetricsSetProtoFileExporter exporter(options); + + Metric metric1{ + .name = "test_metric1", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name1", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}}, + .stats = + Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + Metric metric2{ + .name = "test_metric2", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kSmallerIsBetter, + .test_case = "test_case_name2", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}}, + .stats = Metric::Stats{ + .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}}; + + ASSERT_TRUE(exporter.Export(std::vector{metric1, metric2})); + webrtc::test_metrics::MetricsSet actual_metrics_set; + actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_THAT(actual_metrics_set.metrics().size(), Eq(2)); + + EXPECT_THAT(actual_metrics_set.metrics(0).name(), Eq("test_metric1")); + EXPECT_THAT(actual_metrics_set.metrics(0).test_case(), Eq("test_case_name1")); + EXPECT_THAT(actual_metrics_set.metrics(0).unit(), + Eq(proto::Unit::MILLISECONDS)); + EXPECT_THAT(actual_metrics_set.metrics(0).improvement_direction(), + Eq(proto::ImprovementDirection::BIGGER_IS_BETTER)); + EXPECT_THAT(actual_metrics_set.metrics(0).metric_metadata().size(), Eq(1lu)); + EXPECT_THAT(actual_metrics_set.metrics(0).metric_metadata().at("key"), + Eq("value")); + EXPECT_THAT(actual_metrics_set.metrics(0).time_series().samples().size(), + Eq(2)); + AssertSamplesEqual(actual_metrics_set.metrics(0).time_series().samples(0), + Sample(10.0)); + AssertSamplesEqual(actual_metrics_set.metrics(0).time_series().samples(1), + Sample(20.0)); + EXPECT_THAT(actual_metrics_set.metrics(0).stats().mean(), Eq(15.0)); + EXPECT_THAT(actual_metrics_set.metrics(0).stats().stddev(), Eq(5.0)); + EXPECT_THAT(actual_metrics_set.metrics(0).stats().min(), Eq(10.0)); + EXPECT_THAT(actual_metrics_set.metrics(0).stats().max(), Eq(20.0)); + + EXPECT_THAT(actual_metrics_set.metrics(1).name(), Eq("test_metric2")); + EXPECT_THAT(actual_metrics_set.metrics(1).test_case(), Eq("test_case_name2")); + EXPECT_THAT(actual_metrics_set.metrics(1).unit(), + Eq(proto::Unit::KILOBITS_PER_SECOND)); + EXPECT_THAT(actual_metrics_set.metrics(1).improvement_direction(), + Eq(proto::ImprovementDirection::SMALLER_IS_BETTER)); + EXPECT_THAT(actual_metrics_set.metrics(1).metric_metadata().size(), Eq(1lu)); + EXPECT_THAT(actual_metrics_set.metrics(1).metric_metadata().at("key"), + Eq("value")); + EXPECT_THAT(actual_metrics_set.metrics(1).time_series().samples().size(), + Eq(2)); + AssertSamplesEqual(actual_metrics_set.metrics(1).time_series().samples(0), + Sample(20.0)); + AssertSamplesEqual(actual_metrics_set.metrics(1).time_series().samples(1), + Sample(40.0)); + EXPECT_THAT(actual_metrics_set.metrics(1).stats().mean(), Eq(30.0)); + EXPECT_THAT(actual_metrics_set.metrics(1).stats().stddev(), Eq(10.0)); + EXPECT_THAT(actual_metrics_set.metrics(1).stats().min(), Eq(20.0)); + EXPECT_THAT(actual_metrics_set.metrics(1).stats().max(), Eq(40.0)); +} + +TEST_F(MetricsSetProtoFileExporterTest, NoMetricsSetMetadata) { + MetricsSetProtoFileExporter::Options options(temp_filename_); + MetricsSetProtoFileExporter exporter(options); + ASSERT_TRUE(exporter.Export(std::vector{})); + webrtc::test_metrics::MetricsSet actual_metrics_set; + actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_EQ(actual_metrics_set.metadata_size(), 0); +} + +TEST_F(MetricsSetProtoFileExporterTest, MetricsSetMetadata) { + MetricsSetProtoFileExporter::Options options( + temp_filename_, {{"a_metadata_key", "a_metadata_value"}}); + MetricsSetProtoFileExporter exporter(options); + ASSERT_TRUE(exporter.Export(std::vector{})); + webrtc::test_metrics::MetricsSet actual_metrics_set; + actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_)); + EXPECT_EQ(actual_metrics_set.metadata_size(), 1); + EXPECT_EQ(actual_metrics_set.metadata().at("a_metadata_key"), + "a_metadata_value"); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/print_result_proxy_metrics_exporter.cc b/api/test/metrics/print_result_proxy_metrics_exporter.cc new file mode 100644 index 0000000000..1ce1e63892 --- /dev/null +++ b/api/test/metrics/print_result_proxy_metrics_exporter.cc @@ -0,0 +1,157 @@ +/* + * 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/metrics/print_result_proxy_metrics_exporter.h" + +#include +#include + +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "test/testsupport/perf_test.h" + +namespace webrtc { +namespace test { +namespace { + +std::string ToPrintResultUnit(Unit unit) { + switch (unit) { + case Unit::kMilliseconds: + return "msBestFitFormat"; + case Unit::kPercent: + return "n%"; + case Unit::kBytes: + return "sizeInBytes"; + case Unit::kKilobitsPerSecond: + // PrintResults prefer Chrome Perf Dashboard units, which doesn't have + // kpbs units, so we change the unit and value accordingly. + return "bytesPerSecond"; + case Unit::kHertz: + return "Hz"; + case Unit::kUnitless: + return "unitless"; + case Unit::kCount: + return "count"; + } +} + +double ToPrintResultValue(double value, Unit unit) { + switch (unit) { + case Unit::kKilobitsPerSecond: + // PrintResults prefer Chrome Perf Dashboard units, which doesn't have + // kpbs units, so we change the unit and value accordingly. + return value * 1000 / 8; + default: + return value; + } +} + +ImproveDirection ToPrintResultImproveDirection(ImprovementDirection direction) { + switch (direction) { + case ImprovementDirection::kBiggerIsBetter: + return ImproveDirection::kBiggerIsBetter; + case ImprovementDirection::kNeitherIsBetter: + return ImproveDirection::kNone; + case ImprovementDirection::kSmallerIsBetter: + return ImproveDirection::kSmallerIsBetter; + } +} + +bool IsEmpty(const Metric::Stats& stats) { + return !stats.mean.has_value() && !stats.stddev.has_value() && + !stats.min.has_value() && !stats.max.has_value(); +} + +bool NameEndsWithConnected(const std::string& name) { + static const std::string suffix = "_connected"; + return name.size() >= suffix.size() && + 0 == name.compare(name.size() - suffix.size(), suffix.size(), suffix); +} + +} // namespace + +bool PrintResultProxyMetricsExporter::Export( + rtc::ArrayView metrics) { + static const std::unordered_set per_call_metrics{ + "actual_encode_bitrate", + "encode_frame_rate", + "harmonic_framerate", + "max_skipped", + "min_psnr_dB", + "retransmission_bitrate", + "sent_packets_loss", + "transmission_bitrate", + "dropped_frames", + "frames_in_flight", + "rendered_frames", + "average_receive_rate", + "average_send_rate", + "bytes_discarded_no_receiver", + "bytes_received", + "bytes_sent", + "packets_discarded_no_receiver", + "packets_received", + "packets_sent", + "payload_bytes_received", + "payload_bytes_sent", + "cpu_usage"}; + + for (const Metric& metric : metrics) { + if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) { + // If there were no data collected for the metric it is expected that 0 + // will be exported, so add 0 to the samples. + PrintResult(metric.name, /*modifier=*/"", metric.test_case, + ToPrintResultValue(0, metric.unit), + ToPrintResultUnit(metric.unit), /*important=*/false, + ToPrintResultImproveDirection(metric.improvement_direction)); + continue; + } + + if (metric.time_series.samples.empty()) { + PrintResultMeanAndError( + metric.name, /*modifier=*/"", metric.test_case, + ToPrintResultValue(*metric.stats.mean, metric.unit), + ToPrintResultValue(*metric.stats.stddev, metric.unit), + ToPrintResultUnit(metric.unit), + /*important=*/false, + ToPrintResultImproveDirection(metric.improvement_direction)); + continue; + } + + if (metric.time_series.samples.size() == 1lu && + (per_call_metrics.count(metric.name) > 0 || + NameEndsWithConnected(metric.name))) { + // Increase backwards compatibility for 1 value use case. + PrintResult( + metric.name, /*modifier=*/"", metric.test_case, + ToPrintResultValue(metric.time_series.samples[0].value, metric.unit), + ToPrintResultUnit(metric.unit), /*important=*/false, + ToPrintResultImproveDirection(metric.improvement_direction)); + continue; + } + + SamplesStatsCounter counter; + for (size_t i = 0; i < metric.time_series.samples.size(); ++i) { + counter.AddSample(SamplesStatsCounter::StatsSample{ + .value = ToPrintResultValue(metric.time_series.samples[i].value, + metric.unit), + .time = metric.time_series.samples[i].timestamp, + .metadata = metric.time_series.samples[i].sample_metadata}); + } + + PrintResult(metric.name, /*modifier=*/"", metric.test_case, counter, + ToPrintResultUnit(metric.unit), + /*important=*/false, + ToPrintResultImproveDirection(metric.improvement_direction)); + } + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/print_result_proxy_metrics_exporter.h b/api/test/metrics/print_result_proxy_metrics_exporter.h new file mode 100644 index 0000000000..bad0594972 --- /dev/null +++ b/api/test/metrics/print_result_proxy_metrics_exporter.h @@ -0,0 +1,32 @@ +/* + * 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_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_ +#define API_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_ + +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_exporter.h" + +namespace webrtc { +namespace test { + +// Proxies all exported metrics to the `webrtc::test::PrintResult` API. +class PrintResultProxyMetricsExporter : public MetricsExporter { + public: + ~PrintResultProxyMetricsExporter() override = default; + + bool Export(rtc::ArrayView metrics) override; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_ diff --git a/api/test/metrics/print_result_proxy_metrics_exporter_test.cc b/api/test/metrics/print_result_proxy_metrics_exporter_test.cc new file mode 100644 index 0000000000..768c794b40 --- /dev/null +++ b/api/test/metrics/print_result_proxy_metrics_exporter_test.cc @@ -0,0 +1,177 @@ +/* + * 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/metrics/print_result_proxy_metrics_exporter.h" + +#include +#include +#include + +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::TestWithParam; + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +Metric::TimeSeries::Sample Sample(double value) { + return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1), + .value = value, + .sample_metadata = DefaultMetadata()}; +} + +TEST(PrintResultProxyMetricsExporterTest, + ExportMetricsWithTimeSeriesFormatCorrect) { + Metric metric1{ + .name = "test_metric1", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name1", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}}, + .stats = + Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + Metric metric2{ + .name = "test_metric2", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kSmallerIsBetter, + .test_case = "test_case_name2", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}}, + .stats = Metric::Stats{ + .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}}; + + testing::internal::CaptureStdout(); + PrintResultProxyMetricsExporter exporter; + + std::string expected = + "RESULT test_metric1: test_case_name1= {15,5} " + "msBestFitFormat_biggerIsBetter\n" + "RESULT test_metric2: test_case_name2= {3750,1250} " + "bytesPerSecond_smallerIsBetter\n"; + + EXPECT_TRUE(exporter.Export(std::vector{metric1, metric2})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(PrintResultProxyMetricsExporterTest, + ExportMetricsTimeSeriesOfSingleValueBackwardCompatibleFormat) { + // This should be printed as {mean, stddev} despite only being a single data + // point. + Metric metric1{ + .name = "available_send_bandwidth", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case/alice", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = std::vector{Sample(1000)}}, + .stats = Metric::Stats{ + .mean = 1000.0, .stddev = 0.0, .min = 1000.0, .max = 1000.0}}; + // This is a per-call metric that shouldn't have a stddev estimate. + Metric metric2{ + .name = "min_psnr_dB", + .unit = Unit::kUnitless, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case/alice-video", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = std::vector{Sample(10)}}, + .stats = + Metric::Stats{.mean = 10.0, .stddev = 0.0, .min = 10.0, .max = 10.0}}; + // This is a per-call metric that shouldn't have a stddev estimate. + Metric metric3{ + .name = "alice_connected", + .unit = Unit::kUnitless, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = std::vector{Sample(1)}}, + .stats = + Metric::Stats{.mean = 1.0, .stddev = 0.0, .min = 1.0, .max = 1.0}}; + + testing::internal::CaptureStdout(); + PrintResultProxyMetricsExporter exporter; + + std::string expected = + "RESULT available_send_bandwidth: test_case/alice= {125000,0} " + "bytesPerSecond_biggerIsBetter\n" + "RESULT min_psnr_dB: test_case/alice-video= 10 " + "unitless_biggerIsBetter\n" + "RESULT alice_connected: test_case= 1 " + "unitless_biggerIsBetter\n"; + + EXPECT_TRUE(exporter.Export(std::vector{metric1, metric2, metric3})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(PrintResultProxyMetricsExporterTest, + ExportMetricsWithStatsOnlyFormatCorrect) { + Metric metric1{.name = "test_metric1", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name1", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{ + .mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + Metric metric2{ + .name = "test_metric2", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kSmallerIsBetter, + .test_case = "test_case_name2", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{ + .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}}; + + testing::internal::CaptureStdout(); + PrintResultProxyMetricsExporter exporter; + + std::string expected = + "RESULT test_metric1: test_case_name1= {15,5} " + "msBestFitFormat_biggerIsBetter\n" + "RESULT test_metric2: test_case_name2= {3750,1250} " + "bytesPerSecond_smallerIsBetter\n"; + + EXPECT_TRUE(exporter.Export(std::vector{metric1, metric2})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(PrintResultProxyMetricsExporterTest, ExportEmptyMetricOnlyFormatCorrect) { + Metric metric{.name = "test_metric", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name", + .metric_metadata = DefaultMetadata(), + .time_series = Metric::TimeSeries{.samples = {}}, + .stats = Metric::Stats{}}; + + testing::internal::CaptureStdout(); + PrintResultProxyMetricsExporter exporter; + + std::string expected = + "RESULT test_metric: test_case_name= 0 " + "msBestFitFormat_biggerIsBetter\n"; + + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/proto/metric.proto b/api/test/metrics/proto/metric.proto new file mode 100644 index 0000000000..94921a57cb --- /dev/null +++ b/api/test/metrics/proto/metric.proto @@ -0,0 +1,89 @@ +/* + * 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. + */ + +syntax = "proto3"; + +package webrtc.test_metrics; + +// Root message of the proto file. Contains collection of all the metrics. +message MetricsSet { + repeated Metric metrics = 1; + // Metadata associated with the whole metrics set. + map metadata = 2; +} + +enum Unit { + // Default value that has to be defined. + UNDEFINED_UNIT = 0; + // General unitless value. Can be used either for dimensionless quantities + // (ex ratio) or for units not presented in this enum and too specific to add + // to this enum. + UNITLESS = 1; + MILLISECONDS = 2; + PERCENT = 3; + BYTES = 4; + KILOBITS_PER_SECOND = 5; + HERTZ = 6; + COUNT = 7; +} + +enum ImprovementDirection { + // Default value that has to be defined. + UNDEFINED_IMPROVEMENT_DIRECTION = 0; + BIGGER_IS_BETTER = 1; + NEITHER_IS_BETTER = 2; + SMALLER_IS_BETTER = 3; +} + +// Single performance metric with all related metadata. +message Metric { + // Metric name, for example PSNR, SSIM, decode_time, etc. + string name = 1; + Unit unit = 2; + ImprovementDirection improvement_direction = 3; + // If the metric is generated by a test, this field can be used to specify + // this information. + string test_case = 4; + // Metadata associated with the whole metric. + map metric_metadata = 5; + + message TimeSeries { + message Sample { + // Timestamp in microseconds associated with a sample. For example, + // the timestamp when the sample was collected. + int64 timestamp_us = 1; + double value = 2; + // Metadata associated with this particular sample. + map sample_metadata = 3; + } + // All samples collected for this metric. It can be empty if the Metric + // object only contains `stats`. + repeated Sample samples = 1; + } + // Contains all samples of the metric collected during test execution. + // It can be empty if the user only stores precomputed statistics into + // `stats`. + TimeSeries time_series = 6; + + // Contains metric's precomputed statistics based on the `time_series` or if + // `time_series` is omitted (has 0 samples) contains precomputed statistics + // provided by the metric's calculator. + message Stats { + // Sample mean of the metric + // (https://en.wikipedia.org/wiki/Sample_mean_and_covariance). + optional double mean = 1; + // Standard deviation (https://en.wikipedia.org/wiki/Standard_deviation). + // Is undefined if `time_series` contains only a single sample. + optional double stddev = 2; + optional double min = 3; + optional double max = 4; + } + Stats stats = 7; +} diff --git a/api/test/metrics/stdout_metrics_exporter.cc b/api/test/metrics/stdout_metrics_exporter.cc new file mode 100644 index 0000000000..22243e73e8 --- /dev/null +++ b/api/test/metrics/stdout_metrics_exporter.cc @@ -0,0 +1,101 @@ +/* + * 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/metrics/stdout_metrics_exporter.h" + +#include + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +namespace test { +namespace { + +// Returns positive integral part of the number. +int64_t IntegralPart(double value) { + return std::lround(std::floor(std::abs(value))); +} + +void AppendWithPrecision(double value, + int digits_after_comma, + rtc::StringBuilder& out) { + int64_t multiplier = std::lround(std::pow(10, digits_after_comma)); + int64_t integral_part = IntegralPart(value); + double decimal_part = std::abs(value) - integral_part; + + // If decimal part has leading zeros then when it will be multiplied on + // `multiplier`, leading zeros will be lost. To preserve them we add "1" + // so then leading digit will be greater than 0 and won't be removed. + // + // During conversion to the string leading digit has to be stripped. + // + // Also due to rounding it may happen that leading digit may be incremented, + // like with `digits_after_comma` 3 number 1.9995 will be rounded to 2. In + // such case this increment has to be propagated to the `integral_part`. + int64_t decimal_holder = std::lround((1 + decimal_part) * multiplier); + if (decimal_holder >= 2 * multiplier) { + // Rounding incremented added leading digit, so we need to transfer 1 to + // integral part. + integral_part++; + decimal_holder -= multiplier; + } + // Remove trailing zeros. + while (decimal_holder % 10 == 0) { + decimal_holder /= 10; + } + + // Print serialized number to output. + if (value < 0) { + out << "-"; + } + out << integral_part; + if (decimal_holder != 1) { + out << "." << std::to_string(decimal_holder).substr(1, digits_after_comma); + } +} + +} // namespace + +StdoutMetricsExporter::StdoutMetricsExporter() : output_(stdout) {} + +bool StdoutMetricsExporter::Export(rtc::ArrayView metrics) { + for (const Metric& metric : metrics) { + PrintMetric(metric); + } + return true; +} + +void StdoutMetricsExporter::PrintMetric(const Metric& metric) { + rtc::StringBuilder value_stream; + value_stream << metric.test_case << " / " << metric.name << "= {mean="; + if (metric.stats.mean.has_value()) { + AppendWithPrecision(*metric.stats.mean, 8, value_stream); + } else { + value_stream << "-"; + } + value_stream << ", stddev="; + if (metric.stats.stddev.has_value()) { + AppendWithPrecision(*metric.stats.stddev, 8, value_stream); + } else { + value_stream << "-"; + } + value_stream << "} " << ToString(metric.unit) << " (" + << ToString(metric.improvement_direction) << ")"; + + fprintf(output_, "RESULT: %s\n", value_stream.str().c_str()); +} + +} // namespace test +} // namespace webrtc diff --git a/api/test/metrics/stdout_metrics_exporter.h b/api/test/metrics/stdout_metrics_exporter.h new file mode 100644 index 0000000000..2c572cb2ea --- /dev/null +++ b/api/test/metrics/stdout_metrics_exporter.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 API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_ +#define API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_ + +#include "api/array_view.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_exporter.h" + +namespace webrtc { +namespace test { + +// Exports all collected metrics to stdout. +class StdoutMetricsExporter : public MetricsExporter { + public: + StdoutMetricsExporter(); + ~StdoutMetricsExporter() override = default; + + StdoutMetricsExporter(const StdoutMetricsExporter&) = delete; + StdoutMetricsExporter& operator=(const StdoutMetricsExporter&) = delete; + + bool Export(rtc::ArrayView metrics) override; + + private: + void PrintMetric(const Metric& metric); + + FILE* const output_; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_ diff --git a/api/test/metrics/stdout_metrics_exporter_test.cc b/api/test/metrics/stdout_metrics_exporter_test.cc new file mode 100644 index 0000000000..91c06fac5b --- /dev/null +++ b/api/test/metrics/stdout_metrics_exporter_test.cc @@ -0,0 +1,211 @@ +/* + * 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/metrics/stdout_metrics_exporter.h" + +#include +#include +#include + +#include "api/test/metrics/metric.h" +#include "api/units/timestamp.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace test { +namespace { + +using ::testing::TestWithParam; + +std::map DefaultMetadata() { + return std::map{{"key", "value"}}; +} + +Metric::TimeSeries::Sample Sample(double value) { + return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1), + .value = value, + .sample_metadata = DefaultMetadata()}; +} + +Metric PsnrForTestFoo(double mean, double stddev) { + return Metric{.name = "psnr", + .unit = Unit::kUnitless, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "foo", + .time_series = Metric::TimeSeries{}, + .stats = Metric::Stats{.mean = mean, .stddev = stddev}}; +} + +TEST(StdoutMetricsExporterTest, ExportMetricFormatCorrect) { + Metric metric1{ + .name = "test_metric1", + .unit = Unit::kMilliseconds, + .improvement_direction = ImprovementDirection::kBiggerIsBetter, + .test_case = "test_case_name1", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}}, + .stats = + Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}}; + Metric metric2{ + .name = "test_metric2", + .unit = Unit::kKilobitsPerSecond, + .improvement_direction = ImprovementDirection::kSmallerIsBetter, + .test_case = "test_case_name2", + .metric_metadata = DefaultMetadata(), + .time_series = + Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}}, + .stats = Metric::Stats{ + .mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}}; + + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + std::string expected = + "RESULT: test_case_name1 / test_metric1= " + "{mean=15, stddev=5} Milliseconds (BiggerIsBetter)\n" + "RESULT: test_case_name2 / test_metric2= " + "{mean=30, stddev=10} KilobitsPerSecond (SmallerIsBetter)\n"; + + EXPECT_TRUE(exporter.Export(std::vector{metric1, metric2})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, PositiveNumberMaxPrecision) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(15.00000001, 0.00000001); + std::string expected = + "RESULT: foo / psnr= " + "{mean=15.00000001, stddev=0.00000001} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + PositiveNumberTrailingZeroNotAdded) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(15.12345, 0.12); + std::string expected = + "RESULT: foo / psnr= " + "{mean=15.12345, stddev=0.12} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + PositiveNumberTrailingZeroAreRemoved) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(15.123450000, 0.120000000); + std::string expected = + "RESULT: foo / psnr= " + "{mean=15.12345, stddev=0.12} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + PositiveNumberRoundsUpOnPrecisionCorrectly) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(15.000000009, 0.999999999); + std::string expected = + "RESULT: foo / psnr= " + "{mean=15.00000001, stddev=1} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + PositiveNumberRoundsDownOnPrecisionCorrectly) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(15.0000000049, 0.9999999949); + std::string expected = + "RESULT: foo / psnr= " + "{mean=15, stddev=0.99999999} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, NegativeNumberMaxPrecision) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(-15.00000001, -0.00000001); + std::string expected = + "RESULT: foo / psnr= " + "{mean=-15.00000001, stddev=-0.00000001} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + NegativeNumberTrailingZeroNotAdded) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(-15.12345, -0.12); + std::string expected = + "RESULT: foo / psnr= " + "{mean=-15.12345, stddev=-0.12} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + NegativeNumberTrailingZeroAreRemoved) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(-15.123450000, -0.120000000); + std::string expected = + "RESULT: foo / psnr= " + "{mean=-15.12345, stddev=-0.12} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + NegativeNumberRoundsUpOnPrecisionCorrectly) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(-15.000000009, -0.999999999); + std::string expected = + "RESULT: foo / psnr= " + "{mean=-15.00000001, stddev=-1} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +TEST(StdoutMetricsExporterNumberFormatTest, + NegativeNumberRoundsDownOnPrecisionCorrectly) { + testing::internal::CaptureStdout(); + StdoutMetricsExporter exporter; + + Metric metric = PsnrForTestFoo(-15.0000000049, -0.9999999949); + std::string expected = + "RESULT: foo / psnr= " + "{mean=-15, stddev=-0.99999999} Unitless (BiggerIsBetter)\n"; + EXPECT_TRUE(exporter.Export(std::vector{metric})); + EXPECT_EQ(expected, testing::internal::GetCapturedStdout()); +} + +} // namespace +} // namespace test +} // namespace webrtc diff --git a/api/test/mock_async_dns_resolver.h b/api/test/mock_async_dns_resolver.h index 7cc17a8427..81132c96a5 100644 --- a/api/test/mock_async_dns_resolver.h +++ b/api/test/mock_async_dns_resolver.h @@ -34,6 +34,10 @@ class MockAsyncDnsResolver : public AsyncDnsResolverInterface { Start, (const rtc::SocketAddress&, std::function), (override)); + MOCK_METHOD(void, + Start, + (const rtc::SocketAddress&, int family, std::function), + (override)); MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const, override)); }; @@ -43,6 +47,10 @@ class MockAsyncDnsResolverFactory : public AsyncDnsResolverFactoryInterface { CreateAndResolve, (const rtc::SocketAddress&, std::function), (override)); + MOCK_METHOD(std::unique_ptr, + CreateAndResolve, + (const rtc::SocketAddress&, int, std::function), + (override)); MOCK_METHOD(std::unique_ptr, Create, (), diff --git a/api/test/mock_audio_sink.h b/api/test/mock_audio_sink.h index 0c17dc45ca..88f38a3c57 100644 --- a/api/test/mock_audio_sink.h +++ b/api/test/mock_audio_sink.h @@ -17,7 +17,7 @@ namespace webrtc { -class MockAudioSink final : public webrtc::AudioTrackSinkInterface { +class MockAudioSink : public webrtc::AudioTrackSinkInterface { public: MOCK_METHOD(void, OnData, diff --git a/api/test/mock_data_channel.h b/api/test/mock_data_channel.h index 40f7edb08a..38730eaa51 100644 --- a/api/test/mock_data_channel.h +++ b/api/test/mock_data_channel.h @@ -18,7 +18,7 @@ namespace webrtc { -class MockDataChannelInterface final +class MockDataChannelInterface : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { diff --git a/api/test/mock_encoder_selector.h b/api/test/mock_encoder_selector.h new file mode 100644 index 0000000000..2e018d57ba --- /dev/null +++ b/api/test/mock_encoder_selector.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_TEST_MOCK_ENCODER_SELECTOR_H_ +#define API_TEST_MOCK_ENCODER_SELECTOR_H_ + +#include "api/video_codecs/video_encoder_factory.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockEncoderSelector + : public VideoEncoderFactory::EncoderSelectorInterface { + public: + MOCK_METHOD(void, + OnCurrentEncoder, + (const SdpVideoFormat& format), + (override)); + + MOCK_METHOD(absl::optional, + OnAvailableBitrate, + (const DataRate& rate), + (override)); + + MOCK_METHOD(absl::optional, + OnResolutionChange, + (const RenderResolution& resolution), + (override)); + + MOCK_METHOD(absl::optional, OnEncoderBroken, (), (override)); +}; + +} // namespace webrtc + +#endif // API_TEST_MOCK_ENCODER_SELECTOR_H_ diff --git a/api/test/mock_media_stream_interface.h b/api/test/mock_media_stream_interface.h index 209962358d..dfdbab35e9 100644 --- a/api/test/mock_media_stream_interface.h +++ b/api/test/mock_media_stream_interface.h @@ -18,8 +18,7 @@ namespace webrtc { -class MockAudioSource final - : public rtc::RefCountedObject { +class MockAudioSource : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { return rtc::scoped_refptr(new MockAudioSource()); @@ -52,7 +51,7 @@ class MockAudioSource final MockAudioSource() = default; }; -class MockAudioTrack final : public rtc::RefCountedObject { +class MockAudioTrack : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { return rtc::scoped_refptr(new MockAudioTrack()); diff --git a/api/test/mock_packet_socket_factory.h b/api/test/mock_packet_socket_factory.h new file mode 100644 index 0000000000..7e59556385 --- /dev/null +++ b/api/test/mock_packet_socket_factory.h @@ -0,0 +1,49 @@ +/* + * 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_TEST_MOCK_PACKET_SOCKET_FACTORY_H_ +#define API_TEST_MOCK_PACKET_SOCKET_FACTORY_H_ + +#include +#include + +#include "api/packet_socket_factory.h" +#include "test/gmock.h" + +namespace rtc { +class MockPacketSocketFactory : public PacketSocketFactory { + public: + MOCK_METHOD(AsyncPacketSocket*, + CreateUdpSocket, + (const SocketAddress&, uint16_t, uint16_t), + (override)); + MOCK_METHOD(AsyncListenSocket*, + CreateServerTcpSocket, + (const SocketAddress&, uint16_t, uint16_t, int opts), + (override)); + MOCK_METHOD(AsyncPacketSocket*, + CreateClientTcpSocket, + (const SocketAddress& local_address, + const SocketAddress&, + const ProxyInfo&, + const std::string&, + const PacketSocketTcpOptions&), + (override)); + MOCK_METHOD(std::unique_ptr, + CreateAsyncDnsResolver, + (), + (override)); +}; + +static_assert(!std::is_abstract_v, ""); + +} // namespace rtc + +#endif // API_TEST_MOCK_PACKET_SOCKET_FACTORY_H_ diff --git a/api/test/mock_peer_connection_factory_interface.h b/api/test/mock_peer_connection_factory_interface.h index 6bab595b5a..ae1fbfbbb7 100644 --- a/api/test/mock_peer_connection_factory_interface.h +++ b/api/test/mock_peer_connection_factory_interface.h @@ -19,7 +19,7 @@ namespace webrtc { -class MockPeerConnectionFactoryInterface final +class MockPeerConnectionFactoryInterface : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { diff --git a/api/test/mock_peerconnectioninterface.h b/api/test/mock_peerconnectioninterface.h index 97b2b7d7b1..ccc6ce46b1 100644 --- a/api/test/mock_peerconnectioninterface.h +++ b/api/test/mock_peerconnectioninterface.h @@ -47,6 +47,12 @@ class MockPeerConnectionInterface : public webrtc::PeerConnectionInterface { (rtc::scoped_refptr, const std::vector&), (override)); + MOCK_METHOD(RTCErrorOr>, + AddTrack, + (rtc::scoped_refptr, + const std::vector&, + const std::vector&), + (override)); MOCK_METHOD(RTCError, RemoveTrackOrError, (rtc::scoped_refptr), diff --git a/api/test/mock_rtpreceiver.h b/api/test/mock_rtpreceiver.h index 4bcf064b2a..63318dc32d 100644 --- a/api/test/mock_rtpreceiver.h +++ b/api/test/mock_rtpreceiver.h @@ -14,6 +14,7 @@ #include #include +#include "api/crypto/frame_decryptor_interface.h" #include "api/rtp_receiver_interface.h" #include "test/gmock.h" @@ -32,12 +33,24 @@ class MockRtpReceiver : public rtc::RefCountedObject { MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); MOCK_METHOD(std::string, id, (), (const, override)); MOCK_METHOD(RtpParameters, GetParameters, (), (const, override)); + MOCK_METHOD(bool, + SetParameters, + (const webrtc::RtpParameters& parameters), + (override)); MOCK_METHOD(void, SetObserver, (RtpReceiverObserverInterface*), (override)); MOCK_METHOD(void, SetJitterBufferMinimumDelay, (absl::optional), (override)); MOCK_METHOD(std::vector, GetSources, (), (const, override)); + MOCK_METHOD(void, + SetFrameDecryptor, + (rtc::scoped_refptr), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + GetFrameDecryptor, + (), + (const, override)); }; } // namespace webrtc diff --git a/api/test/mock_rtpsender.h b/api/test/mock_rtpsender.h index e36eec4618..22113678b9 100644 --- a/api/test/mock_rtpsender.h +++ b/api/test/mock_rtpsender.h @@ -11,6 +11,7 @@ #ifndef API_TEST_MOCK_RTPSENDER_H_ #define API_TEST_MOCK_RTPSENDER_H_ +#include #include #include @@ -30,20 +31,45 @@ class MockRtpSender : public RtpSenderInterface { track, (), (const, override)); + MOCK_METHOD(rtc::scoped_refptr, + dtls_transport, + (), + (const override)); MOCK_METHOD(uint32_t, ssrc, (), (const, override)); MOCK_METHOD(cricket::MediaType, media_type, (), (const, override)); MOCK_METHOD(std::string, id, (), (const, override)); MOCK_METHOD(std::vector, stream_ids, (), (const, override)); + MOCK_METHOD(void, SetStreams, (const std::vector&), (override)); MOCK_METHOD(std::vector, init_send_encodings, (), (const, override)); MOCK_METHOD(RtpParameters, GetParameters, (), (const, override)); MOCK_METHOD(RTCError, SetParameters, (const RtpParameters&), (override)); + MOCK_METHOD(void, + SetParametersAsync, + (const RtpParameters&, SetParametersCallback), + (override)); MOCK_METHOD(rtc::scoped_refptr, GetDtmfSender, (), (const, override)); + MOCK_METHOD(void, + SetFrameEncryptor, + (rtc::scoped_refptr), + (override)); + MOCK_METHOD(rtc::scoped_refptr, + GetFrameEncryptor, + (), + (const, override)); + MOCK_METHOD(void, + SetEncoderToPacketizerFrameTransformer, + (rtc::scoped_refptr), + (override)); + MOCK_METHOD(void, + SetEncoderSelector, + (std::unique_ptr), + (override)); }; static_assert(!std::is_abstract_v>, ""); diff --git a/api/test/mock_session_description_interface.h b/api/test/mock_session_description_interface.h new file mode 100644 index 0000000000..f0346ceb11 --- /dev/null +++ b/api/test/mock_session_description_interface.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_SESSION_DESCRIPTION_INTERFACE_H_ +#define API_TEST_MOCK_SESSION_DESCRIPTION_INTERFACE_H_ + +#include +#include +#include +#include + +#include "api/jsep.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockSessionDescriptionInterface : public SessionDescriptionInterface { + public: + MOCK_METHOD(std::unique_ptr, + Clone, + (), + (const, override)); + MOCK_METHOD(cricket::SessionDescription*, description, (), (override)); + MOCK_METHOD(const cricket::SessionDescription*, + description, + (), + (const, override)); + MOCK_METHOD(std::string, session_id, (), (const, override)); + MOCK_METHOD(std::string, session_version, (), (const, override)); + MOCK_METHOD(SdpType, GetType, (), (const, override)); + MOCK_METHOD(std::string, type, (), (const, override)); + MOCK_METHOD(bool, AddCandidate, (const IceCandidateInterface*), (override)); + MOCK_METHOD(size_t, + RemoveCandidates, + (const std::vector&), + (override)); + MOCK_METHOD(size_t, number_of_mediasections, (), (const, override)); + MOCK_METHOD(const IceCandidateCollection*, + candidates, + (size_t), + (const, override)); + MOCK_METHOD(bool, ToString, (std::string*), (const, override)); +}; + +static_assert(!std::is_abstract_v); + +} // namespace webrtc + +#endif // API_TEST_MOCK_SESSION_DESCRIPTION_INTERFACE_H_ diff --git a/api/test/mock_transformable_video_frame.h b/api/test/mock_transformable_video_frame.h index 5cebcaba80..a848032dd3 100644 --- a/api/test/mock_transformable_video_frame.h +++ b/api/test/mock_transformable_video_frame.h @@ -31,8 +31,19 @@ class MockTransformableVideoFrame GetMetadata, (), (const, override)); + MOCK_METHOD(void, + SetMetadata, + (const webrtc::VideoFrameMetadata&), + (override)); + MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override)); + MOCK_METHOD(TransformableFrameInterface::Direction, + GetDirection, + (), + (const, override)); }; +static_assert(!std::is_abstract_v, ""); + } // namespace webrtc #endif // API_TEST_MOCK_TRANSFORMABLE_VIDEO_FRAME_H_ diff --git a/api/test/mock_video_decoder.h b/api/test/mock_video_decoder.h index b6d53f8d8d..34f732ca4d 100644 --- a/api/test/mock_video_decoder.h +++ b/api/test/mock_video_decoder.h @@ -11,6 +11,8 @@ #ifndef API_TEST_MOCK_VIDEO_DECODER_H_ #define API_TEST_MOCK_VIDEO_DECODER_H_ +#include + #include "api/video_codecs/video_decoder.h" #include "test/gmock.h" @@ -43,6 +45,8 @@ class MockVideoDecoder : public VideoDecoder { ON_CALL(*this, Configure).WillByDefault(testing::Return(true)); } + ~MockVideoDecoder() override { Destruct(); } + MOCK_METHOD(bool, Configure, (const Settings& settings), (override)); MOCK_METHOD(int32_t, Decode, @@ -55,6 +59,10 @@ class MockVideoDecoder : public VideoDecoder { (DecodedImageCallback * callback), (override)); MOCK_METHOD(int32_t, Release, (), (override)); + + // Special utility method that allows a test to monitor/verify when + // destruction of the decoder instance occurs. + MOCK_METHOD(void, Destruct, (), ()); }; } // namespace webrtc diff --git a/api/test/mock_video_decoder_factory.h b/api/test/mock_video_decoder_factory.h index 98a5d40eb6..6150d9f8b5 100644 --- a/api/test/mock_video_decoder_factory.h +++ b/api/test/mock_video_decoder_factory.h @@ -15,6 +15,7 @@ #include #include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_decoder.h" #include "api/video_codecs/video_decoder_factory.h" #include "test/gmock.h" diff --git a/api/test/mock_video_encoder_factory.h b/api/test/mock_video_encoder_factory.h index 79851096b7..02ee7aa15e 100644 --- a/api/test/mock_video_encoder_factory.h +++ b/api/test/mock_video_encoder_factory.h @@ -15,6 +15,7 @@ #include #include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" #include "test/gmock.h" diff --git a/api/test/mock_video_track.h b/api/test/mock_video_track.h index 705d13509b..1212a32527 100644 --- a/api/test/mock_video_track.h +++ b/api/test/mock_video_track.h @@ -20,7 +20,7 @@ namespace webrtc { -class MockVideoTrack final +class MockVideoTrack : public rtc::RefCountedObject { public: static rtc::scoped_refptr Create() { diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn index f9f26eb5d4..d009d39a21 100644 --- a/api/test/network_emulation/BUILD.gn +++ b/api/test/network_emulation/BUILD.gn @@ -19,10 +19,10 @@ rtc_library("network_emulation") { deps = [ "../..:array_view", - "../../../rtc_base", "../../../rtc_base:checks", "../../../rtc_base:copy_on_write_buffer", "../../../rtc_base:ip_address", + "../../../rtc_base:net_helper", "../../../rtc_base:socket_address", "../../numerics", "../../task_queue", diff --git a/api/test/network_emulation/network_emulation_interfaces.cc b/api/test/network_emulation/network_emulation_interfaces.cc index ac2eb1d971..0f3a7f8ffd 100644 --- a/api/test/network_emulation/network_emulation_interfaces.cc +++ b/api/test/network_emulation/network_emulation_interfaces.cc @@ -12,6 +12,7 @@ #include "rtc_base/net_helper.h" namespace webrtc { + EmulatedIpPacket::EmulatedIpPacket(const rtc::SocketAddress& from, const rtc::SocketAddress& to, rtc::CopyOnWriteBuffer data, @@ -26,4 +27,20 @@ EmulatedIpPacket::EmulatedIpPacket(const rtc::SocketAddress& from, RTC_DCHECK(to.family() == AF_INET || to.family() == AF_INET6); } +DataRate EmulatedNetworkOutgoingStats::AverageSendRate() const { + RTC_DCHECK_GE(packets_sent, 2); + RTC_DCHECK(first_packet_sent_time.IsFinite()); + RTC_DCHECK(last_packet_sent_time.IsFinite()); + return (bytes_sent - first_sent_packet_size) / + (last_packet_sent_time - first_packet_sent_time); +} + +DataRate EmulatedNetworkIncomingStats::AverageReceiveRate() const { + RTC_DCHECK_GE(packets_received, 2); + RTC_DCHECK(first_packet_received_time.IsFinite()); + RTC_DCHECK(last_packet_received_time.IsFinite()); + return (bytes_received - first_received_packet_size) / + (last_packet_received_time - first_packet_received_time); +} + } // namespace webrtc diff --git a/api/test/network_emulation/network_emulation_interfaces.h b/api/test/network_emulation/network_emulation_interfaces.h index 735689c734..7cab07b75d 100644 --- a/api/test/network_emulation/network_emulation_interfaces.h +++ b/api/test/network_emulation/network_emulation_interfaces.h @@ -62,140 +62,182 @@ class EmulatedNetworkReceiverInterface { virtual void OnPacketReceived(EmulatedIpPacket packet) = 0; }; -class EmulatedNetworkOutgoingStats { - public: - virtual ~EmulatedNetworkOutgoingStats() = default; - - virtual int64_t PacketsSent() const = 0; +struct EmulatedNetworkOutgoingStats { + int64_t packets_sent = 0; - virtual DataSize BytesSent() const = 0; + DataSize bytes_sent = DataSize::Zero(); - // Returns the timestamped sizes of all sent packets if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. - // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0; + // Sizes of all sent packets. + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter sent_packets_size; - virtual DataSize FirstSentPacketSize() const = 0; + DataSize first_sent_packet_size = DataSize::Zero(); - // Returns time of the first packet sent or infinite value if no packets were - // sent. - virtual Timestamp FirstPacketSentTime() const = 0; + // Time of the first packet sent or infinite value if no packets were sent. + Timestamp first_packet_sent_time = Timestamp::PlusInfinity(); - // Returns time of the last packet sent or infinite value if no packets were - // sent. - virtual Timestamp LastPacketSentTime() const = 0; + // Time of the last packet sent or infinite value if no packets were sent. + Timestamp last_packet_sent_time = Timestamp::MinusInfinity(); // Returns average send rate. Requires that at least 2 packets were sent. - virtual DataRate AverageSendRate() const = 0; + DataRate AverageSendRate() const; }; -class EmulatedNetworkIncomingStats { - public: - virtual ~EmulatedNetworkIncomingStats() = default; - +struct EmulatedNetworkIncomingStats { // Total amount of packets received with or without destination. - virtual int64_t PacketsReceived() const = 0; + int64_t packets_received = 0; + // Total amount of bytes in received packets. - virtual DataSize BytesReceived() const = 0; - // Returns the timestamped sizes of all received packets if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. - // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0; + DataSize bytes_received = DataSize::Zero(); + + // Sizes of all received packets. + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter received_packets_size; + // Total amount of packets that were received, but no destination was found. - virtual int64_t PacketsDropped() const = 0; - // Total amount of bytes in dropped packets. - virtual DataSize BytesDropped() const = 0; - // Returns the timestamped sizes of all packets that were received, - // but no destination was found if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. - // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0; + int64_t packets_discarded_no_receiver = 0; - virtual DataSize FirstReceivedPacketSize() const = 0; + // Total amount of bytes in discarded packets. + DataSize bytes_discarded_no_receiver = DataSize::Zero(); - // Returns time of the first packet received or infinite value if no packets - // were received. - virtual Timestamp FirstPacketReceivedTime() const = 0; + // Sizes of all packets that were received, but no destination was found. + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter packets_discarded_no_receiver_size; - // Returns time of the last packet received or infinite value if no packets - // were received. - virtual Timestamp LastPacketReceivedTime() const = 0; + DataSize first_received_packet_size = DataSize::Zero(); - virtual DataRate AverageReceiveRate() const = 0; -}; + // Time of the first packet received or infinite value if no packets were + // received. + Timestamp first_packet_received_time = Timestamp::PlusInfinity(); -class EmulatedNetworkStats { - public: - virtual ~EmulatedNetworkStats() = default; + // Time of the last packet received or infinite value if no packets were + // received. + Timestamp last_packet_received_time = Timestamp::MinusInfinity(); - // List of IP addresses that were used to send data considered in this stats - // object. - virtual std::vector LocalAddresses() const = 0; + DataRate AverageReceiveRate() const; +}; - virtual int64_t PacketsSent() const = 0; +struct EmulatedNetworkStats { + int64_t PacketsSent() const { return overall_outgoing_stats.packets_sent; } - virtual DataSize BytesSent() const = 0; - // Returns the timestamped sizes of all sent packets if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. - // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0; - // Returns the timestamped duration between packet was received on - // network interface and was dispatched to the network in microseconds if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + DataSize BytesSent() const { return overall_outgoing_stats.bytes_sent; } + + // Returns the timestamped sizes of all sent packets. // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const = 0; + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + const SamplesStatsCounter& SentPacketsSizeCounter() const { + return overall_outgoing_stats.sent_packets_size; + } + + DataSize FirstSentPacketSize() const { + return overall_outgoing_stats.first_sent_packet_size; + } - virtual DataSize FirstSentPacketSize() const = 0; // Returns time of the first packet sent or infinite value if no packets were // sent. - virtual Timestamp FirstPacketSentTime() const = 0; + Timestamp FirstPacketSentTime() const { + return overall_outgoing_stats.first_packet_sent_time; + } + // Returns time of the last packet sent or infinite value if no packets were // sent. - virtual Timestamp LastPacketSentTime() const = 0; + Timestamp LastPacketSentTime() const { + return overall_outgoing_stats.last_packet_sent_time; + } + + DataRate AverageSendRate() const { + return overall_outgoing_stats.AverageSendRate(); + } - virtual DataRate AverageSendRate() const = 0; // Total amount of packets received regardless of the destination address. - virtual int64_t PacketsReceived() const = 0; + int64_t PacketsReceived() const { + return overall_incoming_stats.packets_received; + } + // Total amount of bytes in received packets. - virtual DataSize BytesReceived() const = 0; - // Returns the timestamped sizes of all received packets if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + DataSize BytesReceived() const { + return overall_incoming_stats.bytes_received; + } + + // Returns the timestamped sizes of all received packets. // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0; + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + const SamplesStatsCounter& ReceivedPacketsSizeCounter() const { + return overall_incoming_stats.received_packets_size; + } + // Total amount of packets that were received, but no destination was found. - virtual int64_t PacketsDropped() const = 0; + int64_t PacketsDiscardedNoReceiver() const { + return overall_incoming_stats.packets_discarded_no_receiver; + } + // Total amount of bytes in dropped packets. - virtual DataSize BytesDropped() const = 0; + DataSize BytesDiscardedNoReceiver() const { + return overall_incoming_stats.bytes_discarded_no_receiver; + } + // Returns counter with timestamped sizes of all packets that were received, - // but no destination was found if - // EmulatedEndpointConfig::stats_gatherming_mode was set to - // StatsGatheringMode::kDebug; otherwise, the returned value will be empty. + // but no destination was found. // Returned reference is valid until the next call to a non-const method. - virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0; + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + const SamplesStatsCounter& PacketsDiscardedNoReceiverSizeCounter() const { + return overall_incoming_stats.packets_discarded_no_receiver_size; + } + + DataSize FirstReceivedPacketSize() const { + return overall_incoming_stats.first_received_packet_size; + } - virtual DataSize FirstReceivedPacketSize() const = 0; // Returns time of the first packet received or infinite value if no packets // were received. - virtual Timestamp FirstPacketReceivedTime() const = 0; + Timestamp FirstPacketReceivedTime() const { + return overall_incoming_stats.first_packet_received_time; + } + // Returns time of the last packet received or infinite value if no packets // were received. - virtual Timestamp LastPacketReceivedTime() const = 0; + Timestamp LastPacketReceivedTime() const { + return overall_incoming_stats.last_packet_received_time; + } + + DataRate AverageReceiveRate() const { + return overall_incoming_stats.AverageReceiveRate(); + } + + // List of IP addresses that were used to send data considered in this stats + // object. + std::vector local_addresses; + + // Overall outgoing stats for all IP addresses which were requested. + EmulatedNetworkOutgoingStats overall_outgoing_stats; - virtual DataRate AverageReceiveRate() const = 0; + // Overall incoming stats for all IP addresses from which data was received + // on requested interfaces. + EmulatedNetworkIncomingStats overall_incoming_stats; - virtual std::map> - OutgoingStatsPerDestination() const = 0; + std::map + outgoing_stats_per_destination; + std::map + incoming_stats_per_source; - virtual std::map> - IncomingStatsPerSource() const = 0; + // Duration between packet was received on network interface and was + // dispatched to the network in microseconds. + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter sent_packets_queue_wait_time_us; +}; + +struct EmulatedNetworkNodeStats { + // Amount of time each packet spent in the emulated network node for which + // stats were collected. + // + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter packet_transport_time; + + // For each packet contains its size divided on the amount of time which it + // spent in the emulated network node for which stats were collected. + // + // Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled. + SamplesStatsCounter size_to_packet_transport_time; }; // EmulatedEndpoint is an abstraction for network interface on device. Instances diff --git a/api/test/network_emulation_manager.cc b/api/test/network_emulation_manager.cc index 9c148a069b..236e2f0e17 100644 --- a/api/test/network_emulation_manager.cc +++ b/api/test/network_emulation_manager.cc @@ -7,13 +7,40 @@ * 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/network_emulation_manager.h" + #include -#include "api/test/network_emulation_manager.h" #include "call/simulated_network.h" +#include "rtc_base/checks.h" namespace webrtc { +bool AbslParseFlag(absl::string_view text, TimeMode* mode, std::string* error) { + if (text == "realtime") { + *mode = TimeMode::kRealTime; + return true; + } + if (text == "simulated") { + *mode = TimeMode::kSimulated; + return true; + } + *error = + "Unknown value for TimeMode enum. Options are 'realtime' or 'simulated'"; + return false; +} + +std::string AbslUnparseFlag(TimeMode mode) { + switch (mode) { + case TimeMode::kRealTime: + return "realtime"; + case TimeMode::kSimulated: + return "simulated"; + } + RTC_CHECK_NOTREACHED(); + return "unknown"; +} + NetworkEmulationManager::SimulatedNetworkNode::Builder& NetworkEmulationManager::SimulatedNetworkNode::Builder::config( BuiltInNetworkBehaviorConfig config) { @@ -74,4 +101,22 @@ NetworkEmulationManager::SimulatedNetworkNode::Builder::Build( res.node = net->CreateEmulatedNode(std::move(behavior)); return res; } + +std::pair +NetworkEmulationManager::CreateEndpointPairWithTwoWayRoutes( + const BuiltInNetworkBehaviorConfig& config) { + auto* alice_node = CreateEmulatedNode(config); + auto* bob_node = CreateEmulatedNode(config); + + auto* alice_endpoint = CreateEndpoint(EmulatedEndpointConfig()); + auto* bob_endpoint = CreateEndpoint(EmulatedEndpointConfig()); + + CreateRoute(alice_endpoint, {alice_node}, bob_endpoint); + CreateRoute(bob_endpoint, {bob_node}, alice_endpoint); + + return { + CreateEmulatedNetworkManagerInterface({alice_endpoint}), + CreateEmulatedNetworkManagerInterface({bob_endpoint}), + }; +} } // namespace webrtc diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h index b5c68af5f3..bc9279d306 100644 --- a/api/test/network_emulation_manager.h +++ b/api/test/network_emulation_manager.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "api/array_view.h" @@ -48,15 +49,18 @@ class EmulatedNetworkNode; // peer device to another network interface on another peer device. class EmulatedRoute; +enum class EmulatedNetworkStatsGatheringMode { + // Gather main network stats counters. See more details on which particular + // metrics are collected in the `EmulatedNetworkStats` and + // `EmulatedNetworkNodeStats` documentation. + kDefault, + // kDefault + also gather per packet statistics. In this mode more memory + // will be used. + kDebug +}; + struct EmulatedEndpointConfig { enum class IpAddressFamily { kIpv4, kIpv6 }; - enum class StatsGatheringMode { - // Gather main network stats counters. - kDefault, - // kDefault + also gather per packet statistics. In this mode more memory - // will be used. - kDebug - }; // If specified will be used to name endpoint for logging purposes. absl::optional name = absl::nullopt; @@ -69,7 +73,6 @@ struct EmulatedEndpointConfig { bool start_as_enabled = true; // Network type which will be used to represent endpoint to WebRTC. rtc::AdapterType type = rtc::AdapterType::ADAPTER_TYPE_UNKNOWN; - StatsGatheringMode stats_gathering_mode = StatsGatheringMode::kDefault; // Allow endpoint to send packets specifying source IP address different to // the current endpoint IP address. If false endpoint will crash if attempt // to send such packet will be done. @@ -142,12 +145,21 @@ class EmulatedNetworkManagerInterface { // specified `stats_callback`. Callback will be executed on network emulation // internal task queue. virtual void GetStats( - std::function)> stats_callback) - const = 0; + std::function stats_callback) const = 0; }; enum class TimeMode { kRealTime, kSimulated }; +// Called implicitly when parsing an ABSL_FLAG of type TimeMode. +// from the command line flag value `text`. +// Returns `true` and sets `*mode` on success; +// returns `false` and sets `*error` on failure. +bool AbslParseFlag(absl::string_view text, TimeMode* mode, std::string* error); + +// AbslUnparseFlag returns a textual flag value corresponding to the TimeMode +// `mode`. +std::string AbslUnparseFlag(TimeMode mode); + // Provides an API for creating and configuring emulated network layer. // All objects returned by this API are owned by NetworkEmulationManager itself // and will be deleted when manager will be deleted. @@ -187,6 +199,11 @@ class NetworkEmulationManager { // Returns a mode in which underlying time controller operates. virtual TimeMode time_mode() const = 0; + // Creates an emulated network node, which represents ideal network with + // unlimited capacity, no delay and no packet loss. + EmulatedNetworkNode* CreateUnconstrainedEmulatedNode() { + return CreateEmulatedNode(BuiltInNetworkBehaviorConfig()); + } // Creates an emulated network node, which represents single network in // the emulated network layer. Uses default implementation on network behavior // which can be configured with `config`. `random_seed` can be provided to @@ -308,13 +325,19 @@ class NetworkEmulationManager { CreateEmulatedNetworkManagerInterface( const std::vector& endpoints) = 0; - // Passes summarized network stats for specified `endpoints` into specified + // Passes combined network stats for all specified `endpoints` into specified // `stats_callback`. Callback will be executed on network emulation // internal task queue. virtual void GetStats( rtc::ArrayView endpoints, - std::function)> - stats_callback) = 0; + std::function stats_callback) = 0; + + // Passes combined network stats for all specified `nodes` into specified + // `stats_callback`. Callback will be executed on network emulation + // internal task queue. + virtual void GetStats( + rtc::ArrayView nodes, + std::function stats_callback) = 0; // Create a EmulatedTURNServer. // The TURN server has 2 endpoints that need to be connected with routes, @@ -322,6 +345,11 @@ class NetworkEmulationManager { // - GetPeerEndpoint() - the endpoint that is "connected to the internet". virtual EmulatedTURNServerInterface* CreateTURNServer( EmulatedTURNServerConfig config) = 0; + + // Create a pair of EmulatedNetworkManagerInterfaces connected to each other. + std::pair + CreateEndpointPairWithTwoWayRoutes( + const BuiltInNetworkBehaviorConfig& config); }; } // namespace webrtc diff --git a/api/test/pclf/BUILD.gn b/api/test/pclf/BUILD.gn new file mode 100644 index 0000000000..0526478e8b --- /dev/null +++ b/api/test/pclf/BUILD.gn @@ -0,0 +1,114 @@ +# 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("../../../webrtc.gni") + +rtc_source_set("media_configuration") { + visibility = [ "*" ] + testonly = true + sources = [ + "media_configuration.cc", + "media_configuration.h", + ] + + deps = [ + "../..:array_view", + "../..:audio_options_api", + "../..:audio_quality_analyzer_api", + "../..:callfactory_api", + "../..:fec_controller_api", + "../..:frame_generator_api", + "../..:function_view", + "../..:libjingle_peerconnection_api", + "../..:media_stream_interface", + "../..:packet_socket_factory", + "../..:peer_network_dependencies", + "../..:rtp_parameters", + "../..:simulated_network_api", + "../..:stats_observer_interface", + "../..:track_id_stream_info_map", + "../..:video_quality_analyzer_api", + "../../../modules/audio_processing:api", + "../../../rtc_base:checks", + "../../../rtc_base:network", + "../../../rtc_base:rtc_certificate_generator", + "../../../rtc_base:ssl", + "../../../rtc_base:stringutils", + "../../../rtc_base:threading", + "../../../test:fileutils", + "../../../test:video_test_support", + "../../../test/pc/e2e/analyzer/video:video_dumping", + "../../audio:audio_mixer_api", + "../../rtc_event_log", + "../../task_queue", + "../../transport:network_control", + "../../units:time_delta", + "../../video_codecs:video_codecs_api", + "../video:video_frame_writer", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/memory", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("media_quality_test_params") { + visibility = [ "*" ] + testonly = true + sources = [ "media_quality_test_params.h" ] + + deps = [ + ":media_configuration", + "../../../api:callfactory_api", + "../../../api:fec_controller_api", + "../../../api:field_trials_view", + "../../../api:libjingle_peerconnection_api", + "../../../api:packet_socket_factory", + "../../../api/audio:audio_mixer_api", + "../../../api/rtc_event_log", + "../../../api/task_queue", + "../../../api/transport:network_control", + "../../../api/video_codecs:video_codecs_api", + "../../../modules/audio_processing:api", + "../../../p2p:rtc_p2p", + "../../../rtc_base:network", + "../../../rtc_base:rtc_certificate_generator", + "../../../rtc_base:ssl", + "../../../rtc_base:threading", + ] +} + +rtc_library("peer_configurer") { + visibility = [ "*" ] + testonly = true + sources = [ + "peer_configurer.cc", + "peer_configurer.h", + ] + deps = [ + ":media_configuration", + ":media_quality_test_params", + "../../../api:callfactory_api", + "../../../api:create_peer_connection_quality_test_frame_generator", + "../../../api:fec_controller_api", + "../../../api:packet_socket_factory", + "../../../api:peer_network_dependencies", + "../../../api/audio:audio_mixer_api", + "../../../api/rtc_event_log", + "../../../api/task_queue", + "../../../api/transport:network_control", + "../../../api/video_codecs:video_codecs_api", + "../../../modules/audio_processing:api", + "../../../rtc_base:network", + "../../../rtc_base:rtc_certificate_generator", + "../../../rtc_base:ssl", + "../../../rtc_base:threading", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} diff --git a/api/test/pclf/DEPS b/api/test/pclf/DEPS new file mode 100644 index 0000000000..60cc0aeeb3 --- /dev/null +++ b/api/test/pclf/DEPS @@ -0,0 +1,13 @@ +specific_include_rules = { + ".*": [ + "+modules/audio_processing/include/audio_processing.h", + "+rtc_base/checks.h", + "+rtc_base/network.h", + "+rtc_base/rtc_certificate_generator.h", + "+rtc_base/ssl_certificate.h", + "+rtc_base/thread.h", + ], + "media_quality_test_params\.h": [ + "+p2p/base/port_allocator.h", + ], +} diff --git a/api/test/pclf/media_configuration.cc b/api/test/pclf/media_configuration.cc new file mode 100644 index 0000000000..56b9e52e01 --- /dev/null +++ b/api/test/pclf/media_configuration.cc @@ -0,0 +1,314 @@ +/* + * 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/pclf/media_configuration.h" + +#include +#include + +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/test/video/video_frame_writer.h" +#include "rtc_base/checks.h" +#include "rtc_base/strings/string_builder.h" +#include "test/pc/e2e/analyzer/video/video_dumping.h" +#include "test/testsupport/file_utils.h" +#include "test/testsupport/video_frame_writer.h" + +namespace webrtc { +namespace webrtc_pc_e2e { +namespace { + +std::string SpecToString(VideoResolution::Spec spec) { + switch (spec) { + case VideoResolution::Spec::kNone: + return "None"; + case VideoResolution::Spec::kMaxFromSender: + return "MaxFromSender"; + } +} + +void AppendResolution(const VideoResolution& resolution, + rtc::StringBuilder& builder) { + builder << "_" << resolution.width() << "x" << resolution.height() << "_" + << resolution.fps(); +} + +} // namespace + +ScreenShareConfig::ScreenShareConfig(TimeDelta slide_change_interval) + : slide_change_interval(slide_change_interval) { + RTC_CHECK_GT(slide_change_interval.ms(), 0); +} +VideoSimulcastConfig::VideoSimulcastConfig(int simulcast_streams_count) + : simulcast_streams_count(simulcast_streams_count) { + RTC_CHECK_GT(simulcast_streams_count, 1); +} +EmulatedSFUConfig::EmulatedSFUConfig(int target_layer_index) + : target_layer_index(target_layer_index) { + RTC_CHECK_GE(target_layer_index, 0); +} + +EmulatedSFUConfig::EmulatedSFUConfig(absl::optional target_layer_index, + absl::optional target_temporal_index) + : target_layer_index(target_layer_index), + target_temporal_index(target_temporal_index) { + RTC_CHECK_GE(target_temporal_index.value_or(0), 0); + if (target_temporal_index) + RTC_CHECK_GE(*target_temporal_index, 0); +} + +VideoResolution::VideoResolution(size_t width, size_t height, int32_t fps) + : width_(width), height_(height), fps_(fps), spec_(Spec::kNone) {} +VideoResolution::VideoResolution(Spec spec) + : width_(0), height_(0), fps_(0), spec_(spec) {} + +bool 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_; +} +bool VideoResolution::operator!=(const VideoResolution& other) const { + return !(*this == other); +} + +bool VideoResolution::IsRegular() const { + return spec_ == Spec::kNone; +} +std::string VideoResolution::ToString() const { + rtc::StringBuilder out; + out << "{ width=" << width_ << ", height=" << height_ << ", fps=" << fps_ + << ", spec=" << SpecToString(spec_) << " }"; + return out.Release(); +} + +VideoDumpOptions::VideoDumpOptions( + absl::string_view output_directory, + int sampling_modulo, + bool export_frame_ids, + std::function( + absl::string_view file_name_prefix, + const VideoResolution& resolution)> video_frame_writer_factory) + : output_directory_(output_directory), + sampling_modulo_(sampling_modulo), + export_frame_ids_(export_frame_ids), + video_frame_writer_factory_(video_frame_writer_factory) { + RTC_CHECK_GT(sampling_modulo, 0); +} + +VideoDumpOptions::VideoDumpOptions(absl::string_view output_directory, + bool export_frame_ids) + : VideoDumpOptions(output_directory, + kDefaultSamplingModulo, + export_frame_ids) {} + +std::unique_ptr +VideoDumpOptions::CreateInputDumpVideoFrameWriter( + absl::string_view stream_label, + const VideoResolution& resolution) const { + std::unique_ptr writer = video_frame_writer_factory_( + GetInputDumpFileName(stream_label, resolution), resolution); + absl::optional frame_ids_file = + GetInputFrameIdsDumpFileName(stream_label, resolution); + if (frame_ids_file.has_value()) { + writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file); + } + return writer; +} + +std::unique_ptr +VideoDumpOptions::CreateOutputDumpVideoFrameWriter( + absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const { + std::unique_ptr writer = video_frame_writer_factory_( + GetOutputDumpFileName(stream_label, receiver, resolution), resolution); + absl::optional frame_ids_file = + GetOutputFrameIdsDumpFileName(stream_label, receiver, resolution); + if (frame_ids_file.has_value()) { + writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file); + } + return writer; +} + +std::unique_ptr +VideoDumpOptions::Y4mVideoFrameWriterFactory( + absl::string_view file_name_prefix, + const VideoResolution& resolution) { + return std::make_unique( + std::string(file_name_prefix) + ".y4m", resolution.width(), + resolution.height(), resolution.fps()); +} + +std::string VideoDumpOptions::GetInputDumpFileName( + absl::string_view stream_label, + const VideoResolution& resolution) const { + rtc::StringBuilder file_name; + file_name << stream_label; + AppendResolution(resolution, file_name); + return test::JoinFilename(output_directory_, file_name.Release()); +} + +absl::optional VideoDumpOptions::GetInputFrameIdsDumpFileName( + absl::string_view stream_label, + const VideoResolution& resolution) const { + if (!export_frame_ids_) { + return absl::nullopt; + } + return GetInputDumpFileName(stream_label, resolution) + ".frame_ids.txt"; +} + +std::string VideoDumpOptions::GetOutputDumpFileName( + absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const { + rtc::StringBuilder file_name; + file_name << stream_label << "_" << receiver; + AppendResolution(resolution, file_name); + return test::JoinFilename(output_directory_, file_name.Release()); +} + +absl::optional VideoDumpOptions::GetOutputFrameIdsDumpFileName( + absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const { + if (!export_frame_ids_) { + return absl::nullopt; + } + return GetOutputDumpFileName(stream_label, receiver, resolution) + + ".frame_ids.txt"; +} + +std::string VideoDumpOptions::ToString() const { + rtc::StringBuilder out; + out << "{ output_directory_=" << output_directory_ + << ", sampling_modulo_=" << sampling_modulo_ + << ", export_frame_ids_=" << export_frame_ids_ << " }"; + return out.Release(); +} + +VideoConfig::VideoConfig(const VideoResolution& resolution) + : width(resolution.width()), + height(resolution.height()), + fps(resolution.fps()) { + RTC_CHECK(resolution.IsRegular()); +} +VideoConfig::VideoConfig(size_t width, size_t height, int32_t fps) + : width(width), height(height), fps(fps) {} +VideoConfig::VideoConfig(std::string stream_label, + size_t width, + size_t height, + int32_t fps) + : width(width), + height(height), + fps(fps), + stream_label(std::move(stream_label)) {} + +AudioConfig::AudioConfig(std::string stream_label) + : stream_label(std::move(stream_label)) {} + +VideoCodecConfig::VideoCodecConfig(std::string name) + : name(std::move(name)), required_params() {} +VideoCodecConfig::VideoCodecConfig( + std::string name, + std::map required_params) + : name(std::move(name)), required_params(std::move(required_params)) {} + +absl::optional 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 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; +} + +bool VideoSubscription::operator==(const VideoSubscription& other) const { + return default_resolution_ == other.default_resolution_ && + peers_resolution_ == other.peers_resolution_; +} +bool VideoSubscription::operator!=(const VideoSubscription& other) const { + return !(*this == other); +} + +VideoSubscription& VideoSubscription::SubscribeToPeer( + absl::string_view peer_name, + VideoResolution resolution) { + peers_resolution_[std::string(peer_name)] = resolution; + return *this; +} + +VideoSubscription& VideoSubscription::SubscribeToAllPeers( + VideoResolution resolution) { + default_resolution_ = resolution; + return *this; +} + +absl::optional VideoSubscription::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; +} + +std::vector VideoSubscription::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; +} + +std::string VideoSubscription::ToString() const { + rtc::StringBuilder out; + out << "{ default_resolution_=["; + if (default_resolution_.has_value()) { + out << default_resolution_->ToString(); + } else { + out << "undefined"; + } + out << "], {"; + for (const auto& [peer_name, resolution] : peers_resolution_) { + out << "[" << peer_name << ": " << resolution.ToString() << "], "; + } + out << "} }"; + return out.Release(); +} +} // namespace webrtc_pc_e2e +} // namespace webrtc diff --git a/api/test/pclf/media_configuration.h b/api/test/pclf/media_configuration.h new file mode 100644 index 0000000000..8e841a265b --- /dev/null +++ b/api/test/pclf/media_configuration.h @@ -0,0 +1,484 @@ +/* + * 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_TEST_PCLF_MEDIA_CONFIGURATION_H_ +#define API_TEST_PCLF_MEDIA_CONFIGURATION_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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/audio_options.h" +#include "api/call/call_factory_interface.h" +#include "api/fec_controller.h" +#include "api/function_view.h" +#include "api/media_stream_interface.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/rtp_parameters.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/test/audio_quality_analyzer_interface.h" +#include "api/test/frame_generator_interface.h" +#include "api/test/peer_network_dependencies.h" +#include "api/test/simulated_network.h" +#include "api/test/stats_observer_interface.h" +#include "api/test/track_id_stream_info_map.h" +#include "api/test/video/video_frame_writer.h" +#include "api/test/video_quality_analyzer_interface.h" +#include "api/transport/network_control.h" +#include "api/units/time_delta.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "modules/audio_processing/include/audio_processing.h" +#include "rtc_base/checks.h" +#include "rtc_base/network.h" +#include "rtc_base/rtc_certificate_generator.h" +#include "rtc_base/ssl_certificate.h" +#include "rtc_base/thread.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +constexpr size_t kDefaultSlidesWidth = 1850; +constexpr size_t kDefaultSlidesHeight = 1110; + +// The index of required capturing device in OS provided list of video +// devices. On Linux and Windows the list will be obtained via +// webrtc::VideoCaptureModule::DeviceInfo, on Mac OS via +// [RTCCameraVideoCapturer captureDevices]. +enum class CapturingDeviceIndex : size_t {}; + +// Contains parameters for screen share scrolling. +// +// If scrolling is enabled, then it will be done by putting sliding window +// on source video and moving this window from top left corner to the +// bottom right corner of the picture. +// +// In such case source dimensions must be greater or equal to the sliding +// window dimensions. So `source_width` and `source_height` are the dimensions +// of the source frame, while `VideoConfig::width` and `VideoConfig::height` +// are the dimensions of the sliding window. +// +// Because `source_width` and `source_height` are dimensions of the source +// frame, they have to be width and height of videos from +// `ScreenShareConfig::slides_yuv_file_names`. +// +// Because scrolling have to be done on single slide it also requires, that +// `duration` must be less or equal to +// `ScreenShareConfig::slide_change_interval`. +struct ScrollingParams { + // Duration of scrolling. + TimeDelta duration; + // Width of source slides video. + size_t source_width = kDefaultSlidesWidth; + // Height of source slides video. + size_t source_height = kDefaultSlidesHeight; +}; + +// Contains screen share video stream properties. +struct ScreenShareConfig { + explicit ScreenShareConfig(TimeDelta slide_change_interval); + + // Shows how long one slide should be presented on the screen during + // slide generation. + TimeDelta slide_change_interval; + // If true, slides will be generated programmatically. No scrolling params + // will be applied in such case. + bool generate_slides = false; + // If present scrolling will be applied. Please read extra requirement on + // `slides_yuv_file_names` for scrolling. + absl::optional scrolling_params; + // Contains list of yuv files with slides. + // + // If empty, default set of slides will be used. In such case + // `VideoConfig::width` must be equal to `kDefaultSlidesWidth` and + // `VideoConfig::height` must be equal to `kDefaultSlidesHeight` or if + // `scrolling_params` are specified, then `ScrollingParams::source_width` + // must be equal to `kDefaultSlidesWidth` and + // `ScrollingParams::source_height` must be equal to `kDefaultSlidesHeight`. + std::vector slides_yuv_file_names; +}; + +// 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 +// behavior in regular p2p call. Because of it there are such limitations: +// * if `target_spatial_index` is not equal to the highest spatial layer +// then no packet/frame drops are allowed. +// +// If there will be any drops, that will affect requested layer, then +// WebRTC SVC implementation will continue decoding only the highest +// available layer and won't restore lower layers, so analyzer won't +// receive required data which will cause wrong results or test failures. +struct VideoSimulcastConfig { + explicit VideoSimulcastConfig(int simulcast_streams_count); + + // Specified amount of simulcast streams/SVC layers, depending on which + // encoder is used. + int simulcast_streams_count; +}; + +// Configuration for the emulated Selective Forward Unit (SFU) +// +// The framework can optionally filter out frames that are decoded +// using an emulated SFU. +// When using simulcast or SVC, it's not always desirable to receive +// all frames. In a real world call, a SFU will only forward a subset +// of the frames. +// The emulated SFU is not able to change its configuration dynamically, +// if adaptation happens during the call, layers may be dropped and the +// analyzer won't receive the required data which will cause wrong results or +// test failures. +struct EmulatedSFUConfig { + EmulatedSFUConfig() = default; + explicit EmulatedSFUConfig(int target_layer_index); + EmulatedSFUConfig(absl::optional target_layer_index, + absl::optional target_temporal_index); + + // Specifies simulcast or spatial index of the video stream to analyze. + // There are 2 cases: + // 1. simulcast encoding is used: + // in such case `target_layer_index` will specify the index of + // simulcast stream, that should be analyzed. Other streams will be + // dropped. + // 2. SVC encoding is used: + // in such case `target_layer_index` will specify the top interesting + // spatial layer and all layers below, including target one will be + // processed. All layers above target one will be dropped. + // If not specified then all streams will be received and analyzed. + // When set, it instructs the framework to create an emulated Selective + // Forwarding Unit (SFU) that will propagate only the requested layers. + absl::optional target_layer_index; + // Specifies the index of the maximum temporal unit to keep. + // If not specified then all temporal layers will be received and analyzed. + // When set, it instructs the framework to create an emulated Selective + // Forwarding Unit (SFU) that will propagate only up to the requested layer. + absl::optional target_temporal_index; +}; + +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; + + 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; + + std::string ToString() const; + + private: + size_t width_ = 0; + size_t height_ = 0; + int32_t fps_ = 0; + Spec spec_ = Spec::kNone; +}; + +class VideoDumpOptions { + public: + static constexpr int kDefaultSamplingModulo = 1; + + // output_directory - the output directory where stream will be dumped. The + // output files' names will be constructed as + // __. for output dumps + // and _. for input dumps. + // By default is "y4m". Resolution is in the format + // x_. + // sampling_modulo - the module for the video frames to be dumped. Modulo + // equals X means every Xth frame will be written to the dump file. The + // value must be greater than 0. (Default: 1) + // export_frame_ids - specifies if frame ids should be exported together + // with content of the stream. If true, an output file with the same name as + // video dump and suffix ".frame_ids.txt" will be created. It will contain + // the frame ids in the same order as original frames in the output + // file with stream content. File will contain one frame id per line. + // (Default: false) + // `video_frame_writer_factory` - factory function to create a video frame + // writer for input and output video files. (Default: Y4M video writer + // factory). + explicit VideoDumpOptions( + absl::string_view output_directory, + int sampling_modulo = kDefaultSamplingModulo, + bool export_frame_ids = false, + std::function( + absl::string_view file_name_prefix, + const VideoResolution& resolution)> video_frame_writer_factory = + Y4mVideoFrameWriterFactory); + VideoDumpOptions(absl::string_view output_directory, bool export_frame_ids); + + VideoDumpOptions(const VideoDumpOptions&) = default; + VideoDumpOptions& operator=(const VideoDumpOptions&) = default; + VideoDumpOptions(VideoDumpOptions&&) = default; + VideoDumpOptions& operator=(VideoDumpOptions&&) = default; + + std::string output_directory() const { return output_directory_; } + int sampling_modulo() const { return sampling_modulo_; } + bool export_frame_ids() const { return export_frame_ids_; } + + std::unique_ptr CreateInputDumpVideoFrameWriter( + absl::string_view stream_label, + const VideoResolution& resolution) const; + + std::unique_ptr CreateOutputDumpVideoFrameWriter( + absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const; + + std::string ToString() const; + + private: + static std::unique_ptr Y4mVideoFrameWriterFactory( + absl::string_view file_name_prefix, + const VideoResolution& resolution); + std::string GetInputDumpFileName(absl::string_view stream_label, + const VideoResolution& resolution) const; + // Returns file name for input frame ids dump if `export_frame_ids()` is + // true, absl::nullopt otherwise. + absl::optional GetInputFrameIdsDumpFileName( + absl::string_view stream_label, + const VideoResolution& resolution) const; + std::string GetOutputDumpFileName(absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const; + // Returns file name for output frame ids dump if `export_frame_ids()` is + // true, absl::nullopt otherwise. + absl::optional GetOutputFrameIdsDumpFileName( + absl::string_view stream_label, + absl::string_view receiver, + const VideoResolution& resolution) const; + + std::string output_directory_; + int sampling_modulo_ = 1; + bool export_frame_ids_ = false; + std::function( + absl::string_view file_name_prefix, + const VideoResolution& resolution)> + video_frame_writer_factory_; +}; + +// Contains properties of single video stream. +struct VideoConfig { + explicit VideoConfig(const VideoResolution& resolution); + VideoConfig(size_t width, size_t height, int32_t fps); + VideoConfig(std::string stream_label, + size_t width, + size_t height, + int32_t fps); + + // Video stream width. + size_t width; + // Video stream height. + 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; + // Will be set for current video track. If equals to kText or kDetailed - + // screencast in on. + absl::optional content_hint; + // If presented video will be transfered in simulcast/SVC mode depending on + // which encoder is used. + // + // Simulcast is supported only from 1st added peer. For VP8 simulcast only + // without RTX is supported so it will be automatically disabled for all + // simulcast tracks. For VP9 simulcast enables VP9 SVC mode and support RTX, + // but only on non-lossy networks. See more in documentation to + // VideoSimulcastConfig. + absl::optional simulcast_config; + // Configuration for the emulated Selective Forward Unit (SFU). + absl::optional emulated_sfu_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; + // If specified defines how input should be dumped. 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. It is useful when + // generator is used as input. + absl::optional input_dump_options; + // If specified defines how output should be dumped on the receiver side for + // this stream. The produced files contain what was rendered for this video + // stream on receiver side per each receiver. + absl::optional output_dump_options; + // If set to true uses fixed frame rate while dumping output video to the + // file. Requested `VideoSubscription::fps()` will be used as frame rate. + bool output_dump_use_fixed_framerate = false; + // If true will display input and output video on the user's screen. + bool show_on_screen = false; + // If specified, determines a sync group to which this video stream belongs. + // According to bugs.webrtc.org/4762 WebRTC supports synchronization only + // for pair of single audio and single video stream. + absl::optional sync_group; + // If specified, it will be set into RtpParameters of corresponding + // RtpSenderInterface for this video stream. + // Note that this setting takes precedence over `content_hint`. + absl::optional degradation_preference; +}; + +// Contains properties for audio in the call. +struct AudioConfig { + enum Mode { + kGenerated, + kFile, + }; + + AudioConfig() = default; + explicit AudioConfig(std::string stream_label); + + // Have to be unique among all specified configs for all peers in the call. + // Will be auto generated if omitted. + absl::optional stream_label; + Mode mode = kGenerated; + // Have to be specified only if mode = kFile + absl::optional input_file_name; + // If specified the input stream will be also copied to specified file. + absl::optional input_dump_file_name; + // If specified the output stream will be copied to specified file. + absl::optional output_dump_file_name; + + // Audio options to use. + cricket::AudioOptions audio_options; + // Sampling frequency of input audio data (from file or generated). + int sampling_frequency_in_hz = 48000; + // If specified, determines a sync group to which this audio stream belongs. + // According to bugs.webrtc.org/4762 WebRTC supports synchronization only + // for pair of single audio and single video stream. + absl::optional sync_group; +}; + +struct VideoCodecConfig { + explicit VideoCodecConfig(std::string name); + VideoCodecConfig(std::string name, + std::map required_params); + // Next two fields are used to specify concrete video codec, that should be + // used in the test. Video code will be negotiated in SDP during offer/ + // answer exchange. + // Video codec name. You can find valid names in + // media/base/media_constants.h + std::string name; + // Map of parameters, that have to be specified on SDP codec. Each parameter + // is described by key and value. Codec parameters will match the specified + // map if and only if for each key from `required_params` there will be + // a parameter with name equal to this key and parameter value will be equal + // to the value from `required_params` for this key. + // If empty then only name will be used to match the codec. + 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); + + bool operator==(const VideoSubscription& other) const; + bool operator!=(const VideoSubscription& other) const; + + // 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)); + + // 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)); + + // 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; + + // Returns a maybe empty list of senders for which peer explicitly + // subscribed to with specific resolution. + std::vector GetSubscribedPeers() const; + + std::string ToString() const; + + private: + absl::optional default_resolution_ = absl::nullopt; + std::map peers_resolution_; +}; + +// Contains configuration for echo emulator. +struct EchoEmulationConfig { + // Delay which represents the echo path delay, i.e. how soon rendered signal + // should reach capturer. + TimeDelta echo_delay = TimeDelta::Millis(50); +}; + +} // namespace webrtc_pc_e2e +} // namespace webrtc + +#endif // API_TEST_PCLF_MEDIA_CONFIGURATION_H_ diff --git a/test/pc/e2e/peer_connection_quality_test_params.h b/api/test/pclf/media_quality_test_params.h similarity index 75% rename from test/pc/e2e/peer_connection_quality_test_params.h rename to api/test/pclf/media_quality_test_params.h index 645ed7a290..2485cfca00 100644 --- a/test/pc/e2e/peer_connection_quality_test_params.h +++ b/api/test/pclf/media_quality_test_params.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 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 @@ -7,9 +7,10 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#ifndef TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_PARAMS_H_ -#define TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_PARAMS_H_ +#ifndef API_TEST_PCLF_MEDIA_QUALITY_TEST_PARAMS_H_ +#define API_TEST_PCLF_MEDIA_QUALITY_TEST_PARAMS_H_ +#include #include #include #include @@ -21,7 +22,7 @@ #include "api/field_trials_view.h" #include "api/rtc_event_log/rtc_event_log_factory_interface.h" #include "api/task_queue/task_queue_factory.h" -#include "api/test/peerconnection_quality_test_fixture.h" +#include "api/test/pclf/media_configuration.h" #include "api/transport/network_control.h" #include "api/video_codecs/video_decoder_factory.h" #include "api/video_codecs/video_encoder_factory.h" @@ -56,6 +57,8 @@ struct PeerConnectionFactoryComponents { // PeerConnectionFactory. std::unique_ptr video_encoder_factory; std::unique_ptr video_decoder_factory; + rtc::scoped_refptr audio_encoder_factory; + rtc::scoped_refptr audio_decoder_factory; std::unique_ptr trials; @@ -95,6 +98,7 @@ struct InjectableComponents { rtc::NetworkManager* network_manager, rtc::PacketSocketFactory* packet_socket_factory) : network_thread(network_thread), + worker_thread(nullptr), pcf_dependencies(std::make_unique()), pc_dependencies( std::make_unique(network_manager, @@ -103,6 +107,7 @@ struct InjectableComponents { } rtc::Thread* const network_thread; + rtc::Thread* worker_thread; std::unique_ptr pcf_dependencies; std::unique_ptr pc_dependencies; @@ -115,7 +120,7 @@ struct Params { // Peer name. If empty - default one will be set by the fixture. absl::optional name; // If `audio_config` is set audio stream will be configured - absl::optional audio_config; + absl::optional audio_config; // Flags to set on `cricket::PortAllocator`. These flags will be added // to the default ones that are presented on the port allocator. uint32_t port_allocator_extra_flags = cricket::kDefaultPortAllocatorFlags; @@ -139,21 +144,48 @@ struct Params { PeerConnectionInterface::RTCConfiguration rtc_configuration; PeerConnectionInterface::RTCOfferAnswerOptions rtc_offer_answer_options; BitrateSettings bitrate_settings; - std::vector - video_codecs; + std::vector video_codecs; + + // A list of RTP header extensions which will be enforced on all video streams + // added to this peer. + std::vector extra_video_rtp_header_extensions; + // A list of RTP header extensions which will be enforced on all audio streams + // added to this peer. + std::vector extra_audio_rtp_header_extensions; }; // Contains parameters that maybe changed by test writer during the test call. struct ConfigurableParams { // If `video_configs` is empty - no video should be added to the test call. - std::vector video_configs; + std::vector video_configs; - PeerConnectionE2EQualityTestFixture::VideoSubscription video_subscription = - PeerConnectionE2EQualityTestFixture::VideoSubscription() - .SubscribeToAllPeers(); + VideoSubscription video_subscription = + VideoSubscription().SubscribeToAllPeers(); +}; + +// Contains parameters, that describe how long framework should run quality +// test. +struct RunParams { + explicit RunParams(TimeDelta run_duration) : run_duration(run_duration) {} + + // Specifies how long the test should be run. This time shows how long + // the media should flow after connection was established and before + // it will be shut downed. + TimeDelta run_duration; + + // If set to true peers will be able to use Flex FEC, otherwise they won't + // be able to negotiate it even if it's enabled on per peer level. + bool enable_flex_fec_support = false; + // If true will set conference mode in SDP media section for all video + // tracks for all peers. + bool use_conference_mode = false; + // If specified echo emulation will be done, by mixing the render audio into + // the capture signal. In such case input signal will be reduced by half to + // avoid saturation or compression in the echo path simulation. + absl::optional echo_emulation_config; }; } // namespace webrtc_pc_e2e } // namespace webrtc -#endif // TEST_PC_E2E_PEER_CONNECTION_QUALITY_TEST_PARAMS_H_ +#endif // API_TEST_PCLF_MEDIA_QUALITY_TEST_PARAMS_H_ diff --git a/api/test/pclf/peer_configurer.cc b/api/test/pclf/peer_configurer.cc new file mode 100644 index 0000000000..de7600efc1 --- /dev/null +++ b/api/test/pclf/peer_configurer.cc @@ -0,0 +1,254 @@ +/* + * 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/pclf/peer_configurer.h" + +#include + +#include "absl/strings/string_view.h" +#include "api/test/pclf/media_configuration.h" +#include "api/test/pclf/media_quality_test_params.h" +#include "api/test/peer_network_dependencies.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +PeerConfigurer::PeerConfigurer( + const PeerNetworkDependencies& network_dependencies) + : components_(std::make_unique( + network_dependencies.network_thread, + network_dependencies.network_manager, + network_dependencies.packet_socket_factory)), + params_(std::make_unique()), + configurable_params_(std::make_unique()) {} + +PeerConfigurer* PeerConfigurer::SetName(absl::string_view name) { + params_->name = std::string(name); + return this; +} + +PeerConfigurer* PeerConfigurer::SetTaskQueueFactory( + std::unique_ptr task_queue_factory) { + components_->pcf_dependencies->task_queue_factory = + std::move(task_queue_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetCallFactory( + std::unique_ptr call_factory) { + components_->pcf_dependencies->call_factory = std::move(call_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetEventLogFactory( + std::unique_ptr event_log_factory) { + components_->pcf_dependencies->event_log_factory = + std::move(event_log_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetFecControllerFactory( + std::unique_ptr fec_controller_factory) { + components_->pcf_dependencies->fec_controller_factory = + std::move(fec_controller_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetNetworkControllerFactory( + std::unique_ptr + network_controller_factory) { + components_->pcf_dependencies->network_controller_factory = + std::move(network_controller_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetVideoEncoderFactory( + std::unique_ptr video_encoder_factory) { + components_->pcf_dependencies->video_encoder_factory = + std::move(video_encoder_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetVideoDecoderFactory( + std::unique_ptr video_decoder_factory) { + components_->pcf_dependencies->video_decoder_factory = + std::move(video_decoder_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetAudioEncoderFactory( + rtc::scoped_refptr audio_encoder_factory) { + components_->pcf_dependencies->audio_encoder_factory = audio_encoder_factory; + return this; +} +PeerConfigurer* PeerConfigurer::SetAudioDecoderFactory( + rtc::scoped_refptr audio_decoder_factory) { + components_->pcf_dependencies->audio_decoder_factory = audio_decoder_factory; + return this; +} +PeerConfigurer* PeerConfigurer::SetAsyncResolverFactory( + std::unique_ptr async_resolver_factory) { + components_->pc_dependencies->async_resolver_factory = + std::move(async_resolver_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetRTCCertificateGenerator( + std::unique_ptr cert_generator) { + components_->pc_dependencies->cert_generator = std::move(cert_generator); + return this; +} +PeerConfigurer* PeerConfigurer::SetSSLCertificateVerifier( + std::unique_ptr tls_cert_verifier) { + components_->pc_dependencies->tls_cert_verifier = + std::move(tls_cert_verifier); + return this; +} + +PeerConfigurer* PeerConfigurer::AddVideoConfig(VideoConfig config) { + video_sources_.push_back( + CreateSquareFrameGenerator(config, /*type=*/absl::nullopt)); + configurable_params_->video_configs.push_back(std::move(config)); + return this; +} +PeerConfigurer* PeerConfigurer::AddVideoConfig( + VideoConfig config, + std::unique_ptr generator) { + configurable_params_->video_configs.push_back(std::move(config)); + video_sources_.push_back(std::move(generator)); + return this; +} +PeerConfigurer* PeerConfigurer::AddVideoConfig(VideoConfig config, + CapturingDeviceIndex index) { + configurable_params_->video_configs.push_back(std::move(config)); + video_sources_.push_back(index); + return this; +} +PeerConfigurer* PeerConfigurer::SetVideoSubscription( + VideoSubscription subscription) { + configurable_params_->video_subscription = std::move(subscription); + return this; +} +PeerConfigurer* PeerConfigurer::SetVideoCodecs( + std::vector video_codecs) { + params_->video_codecs = std::move(video_codecs); + return this; +} +PeerConfigurer* PeerConfigurer::SetExtraVideoRtpHeaderExtensions( + std::vector extensions) { + params_->extra_video_rtp_header_extensions = std::move(extensions); + return this; +} +PeerConfigurer* PeerConfigurer::SetAudioConfig(AudioConfig config) { + params_->audio_config = std::move(config); + return this; +} +PeerConfigurer* PeerConfigurer::SetExtraAudioRtpHeaderExtensions( + std::vector extensions) { + params_->extra_audio_rtp_header_extensions = std::move(extensions); + return this; +} +PeerConfigurer* PeerConfigurer::SetUseUlpFEC(bool value) { + params_->use_ulp_fec = value; + return this; +} +PeerConfigurer* PeerConfigurer::SetUseFlexFEC(bool value) { + params_->use_flex_fec = value; + return this; +} +PeerConfigurer* PeerConfigurer::SetVideoEncoderBitrateMultiplier( + double multiplier) { + params_->video_encoder_bitrate_multiplier = multiplier; + return this; +} +PeerConfigurer* PeerConfigurer::SetNetEqFactory( + std::unique_ptr neteq_factory) { + components_->pcf_dependencies->neteq_factory = std::move(neteq_factory); + return this; +} +PeerConfigurer* PeerConfigurer::SetAudioProcessing( + rtc::scoped_refptr audio_processing) { + components_->pcf_dependencies->audio_processing = audio_processing; + return this; +} +PeerConfigurer* PeerConfigurer::SetAudioMixer( + rtc::scoped_refptr audio_mixer) { + components_->pcf_dependencies->audio_mixer = audio_mixer; + return this; +} + +PeerConfigurer* PeerConfigurer::SetUseNetworkThreadAsWorkerThread() { + components_->worker_thread = components_->network_thread; + return this; +} + +PeerConfigurer* PeerConfigurer::SetRtcEventLogPath(std::string path) { + params_->rtc_event_log_path = std::move(path); + return this; +} +PeerConfigurer* PeerConfigurer::SetAecDumpPath(std::string path) { + params_->aec_dump_path = std::move(path); + return this; +} +PeerConfigurer* PeerConfigurer::SetRTCConfiguration( + PeerConnectionInterface::RTCConfiguration configuration) { + params_->rtc_configuration = std::move(configuration); + return this; +} +PeerConfigurer* PeerConfigurer::SetRTCOfferAnswerOptions( + PeerConnectionInterface::RTCOfferAnswerOptions options) { + params_->rtc_offer_answer_options = std::move(options); + return this; +} +PeerConfigurer* PeerConfigurer::SetBitrateSettings( + BitrateSettings bitrate_settings) { + params_->bitrate_settings = bitrate_settings; + return this; +} + +PeerConfigurer* PeerConfigurer::SetIceTransportFactory( + std::unique_ptr factory) { + components_->pc_dependencies->ice_transport_factory = std::move(factory); + return this; +} + +PeerConfigurer* PeerConfigurer::SetPortAllocatorExtraFlags( + uint32_t extra_flags) { + params_->port_allocator_extra_flags = extra_flags; + return this; +} +std::unique_ptr PeerConfigurer::ReleaseComponents() { + RTC_CHECK(components_); + auto components = std::move(components_); + components_ = nullptr; + return components; +} + +// Returns Params and transfer ownership to the caller. +// Can be called once. +std::unique_ptr PeerConfigurer::ReleaseParams() { + RTC_CHECK(params_); + auto params = std::move(params_); + params_ = nullptr; + return params; +} + +// Returns ConfigurableParams and transfer ownership to the caller. +// Can be called once. +std::unique_ptr +PeerConfigurer::ReleaseConfigurableParams() { + RTC_CHECK(configurable_params_); + auto configurable_params = std::move(configurable_params_); + configurable_params_ = nullptr; + return configurable_params; +} + +// Returns video sources and transfer frame generators ownership to the +// caller. Can be called once. +std::vector PeerConfigurer::ReleaseVideoSources() { + auto video_sources = std::move(video_sources_); + video_sources_.clear(); + return video_sources; +} + +} // namespace webrtc_pc_e2e +} // namespace webrtc diff --git a/api/test/pclf/peer_configurer.h b/api/test/pclf/peer_configurer.h new file mode 100644 index 0000000000..10f37a1eec --- /dev/null +++ b/api/test/pclf/peer_configurer.h @@ -0,0 +1,204 @@ +/* + * 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_TEST_PCLF_PEER_CONFIGURER_H_ +#define API_TEST_PCLF_PEER_CONFIGURER_H_ + +#include +#include +#include +#include + +#include "absl/strings/string_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/rtc_event_log/rtc_event_log_factory_interface.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/test/create_peer_connection_quality_test_frame_generator.h" +#include "api/test/pclf/media_configuration.h" +#include "api/test/pclf/media_quality_test_params.h" +#include "api/test/peer_network_dependencies.h" +#include "api/transport/network_control.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.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" +#include "rtc_base/thread.h" + +namespace webrtc { +namespace webrtc_pc_e2e { + +// This class is used to fully configure one peer inside a call. +class PeerConfigurer { + public: + using VideoSource = + absl::variant, + CapturingDeviceIndex>; + + explicit PeerConfigurer(const PeerNetworkDependencies& network_dependencies); + + // Sets peer name that will be used to report metrics related to this peer. + // If not set, some default name will be assigned. All names have to be + // unique. + PeerConfigurer* SetName(absl::string_view name); + + // The parameters of the following 9 methods will be passed to the + // PeerConnectionFactoryInterface implementation that will be created for + // this peer. + PeerConfigurer* SetTaskQueueFactory( + std::unique_ptr task_queue_factory); + PeerConfigurer* SetCallFactory( + std::unique_ptr call_factory); + PeerConfigurer* SetEventLogFactory( + std::unique_ptr event_log_factory); + PeerConfigurer* SetFecControllerFactory( + std::unique_ptr fec_controller_factory); + PeerConfigurer* SetNetworkControllerFactory( + std::unique_ptr + network_controller_factory); + PeerConfigurer* SetVideoEncoderFactory( + std::unique_ptr video_encoder_factory); + PeerConfigurer* SetVideoDecoderFactory( + std::unique_ptr video_decoder_factory); + PeerConfigurer* SetAudioEncoderFactory( + rtc::scoped_refptr audio_encoder_factory); + PeerConfigurer* SetAudioDecoderFactory( + rtc::scoped_refptr audio_decoder_factory); + // Set a custom NetEqFactory to be used in the call. + PeerConfigurer* SetNetEqFactory(std::unique_ptr neteq_factory); + PeerConfigurer* SetAudioProcessing( + rtc::scoped_refptr audio_processing); + PeerConfigurer* SetAudioMixer( + rtc::scoped_refptr audio_mixer); + + // Forces the Peerconnection to use the network thread as the worker thread. + // Ie, worker thread and the network thread is the same thread. + PeerConfigurer* SetUseNetworkThreadAsWorkerThread(); + + // The parameters of the following 4 methods will be passed to the + // PeerConnectionInterface implementation that will be created for this + // peer. + PeerConfigurer* SetAsyncResolverFactory( + std::unique_ptr async_resolver_factory); + PeerConfigurer* SetRTCCertificateGenerator( + std::unique_ptr cert_generator); + PeerConfigurer* SetSSLCertificateVerifier( + std::unique_ptr tls_cert_verifier); + PeerConfigurer* SetIceTransportFactory( + std::unique_ptr factory); + // 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. + PeerConfigurer* SetPortAllocatorExtraFlags(uint32_t extra_flags); + + // Add new video stream to the call that will be sent from this peer. + // Default implementation of video frames generator will be used. + PeerConfigurer* AddVideoConfig(VideoConfig config); + // Add new video stream to the call that will be sent from this peer with + // provided own implementation of video frames generator. + PeerConfigurer* AddVideoConfig( + VideoConfig config, + std::unique_ptr generator); + // Add new video stream to the call that will be sent from this peer. + // Capturing device with specified index will be used to get input video. + PeerConfigurer* AddVideoConfig(VideoConfig config, + CapturingDeviceIndex capturing_device_index); + // Sets video subscription for the peer. By default subscription will + // include all streams with `VideoSubscription::kSameAsSendStream` + // resolution. To this behavior use this method. + PeerConfigurer* SetVideoSubscription(VideoSubscription subscription); + // Sets 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`. + // Codecs have to be available in codecs list provided by peer connection to + // be negotiated. If some of specified codecs won't be found, the test will + // crash. + PeerConfigurer* SetVideoCodecs(std::vector video_codecs); + // Sets a list of RTP header extensions which will be enforced on all video + // streams added to this peer. + PeerConfigurer* SetExtraVideoRtpHeaderExtensions( + std::vector extensions); + // Sets the audio stream for the call from this peer. If this method won't + // be invoked, this peer will send no audio. + PeerConfigurer* SetAudioConfig(AudioConfig config); + // Sets a list of RTP header extensions which will be enforced on all audio + // streams added to this peer. + PeerConfigurer* SetExtraAudioRtpHeaderExtensions( + std::vector extensions); + + // Set if ULP FEC should be used or not. False by default. + PeerConfigurer* SetUseUlpFEC(bool value); + // Set if Flex FEC should be used or not. False by default. + // Client also must enable `enable_flex_fec_support` in the `RunParams` to + // be able to use this feature. + PeerConfigurer* SetUseFlexFEC(bool value); + // Specifies how much video encoder target bitrate should be different than + // target bitrate, provided by WebRTC stack. Must be greater than 0. Can be + // used to emulate overshooting of video encoders. This multiplier will + // be applied for all video encoder on both sides for all layers. Bitrate + // estimated by WebRTC stack will be multiplied by this multiplier and then + // provided into VideoEncoder::SetRates(...). 1.0 by default. + PeerConfigurer* SetVideoEncoderBitrateMultiplier(double multiplier); + + // If is set, an RTCEventLog will be saved in that location and it will be + // available for further analysis. + PeerConfigurer* SetRtcEventLogPath(std::string path); + // If is set, an AEC dump will be saved in that location and it will be + // available for further analysis. + PeerConfigurer* SetAecDumpPath(std::string path); + PeerConfigurer* SetRTCConfiguration( + PeerConnectionInterface::RTCConfiguration configuration); + PeerConfigurer* SetRTCOfferAnswerOptions( + PeerConnectionInterface::RTCOfferAnswerOptions options); + // Set bitrate parameters on PeerConnection. This constraints will be + // applied to all summed RTP streams for this peer. + PeerConfigurer* SetBitrateSettings(BitrateSettings bitrate_settings); + + // Returns InjectableComponents and transfer ownership to the caller. + // Can be called once. + std::unique_ptr ReleaseComponents(); + + // Returns Params and transfer ownership to the caller. + // Can be called once. + std::unique_ptr ReleaseParams(); + + // Returns ConfigurableParams and transfer ownership to the caller. + // Can be called once. + std::unique_ptr ReleaseConfigurableParams(); + + // Returns video sources and transfer frame generators ownership to the + // caller. Can be called once. + std::vector ReleaseVideoSources(); + + InjectableComponents* components() { return components_.get(); } + Params* params() { return params_.get(); } + ConfigurableParams* configurable_params() { + return configurable_params_.get(); + } + const Params& params() const { return *params_; } + const ConfigurableParams& configurable_params() const { + return *configurable_params_; + } + std::vector* video_sources() { return &video_sources_; } + + private: + std::unique_ptr components_; + std::unique_ptr params_; + std::unique_ptr configurable_params_; + std::vector video_sources_; +}; + +} // namespace webrtc_pc_e2e +} // namespace webrtc + +#endif // API_TEST_PCLF_PEER_CONFIGURER_H_ diff --git a/api/test/peerconnection_quality_test_fixture.cc b/api/test/peerconnection_quality_test_fixture.cc deleted file mode 100644 index 59526f9f52..0000000000 --- a/api/test/peerconnection_quality_test_fixture.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 fee911a8b8..74470cdf86 100644 --- a/api/test/peerconnection_quality_test_fixture.h +++ b/api/test/peerconnection_quality_test_fixture.h @@ -10,12 +10,17 @@ #ifndef API_TEST_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_ #define API_TEST_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_ +#include +#include + +#include #include #include #include #include #include +#include "absl/base/macros.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -32,10 +37,14 @@ #include "api/task_queue/task_queue_factory.h" #include "api/test/audio_quality_analyzer_interface.h" #include "api/test/frame_generator_interface.h" +#include "api/test/pclf/media_configuration.h" +#include "api/test/pclf/media_quality_test_params.h" +#include "api/test/pclf/peer_configurer.h" #include "api/test/peer_network_dependencies.h" #include "api/test/simulated_network.h" #include "api/test/stats_observer_interface.h" #include "api/test/track_id_stream_info_map.h" +#include "api/test/video/video_frame_writer.h" #include "api/test/video_quality_analyzer_interface.h" #include "api/transport/network_control.h" #include "api/units/time_delta.h" @@ -44,6 +53,7 @@ #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/checks.h" #include "rtc_base/network.h" #include "rtc_base/rtc_certificate_generator.h" #include "rtc_base/ssl_certificate.h" @@ -52,528 +62,9 @@ namespace webrtc { namespace webrtc_pc_e2e { -constexpr size_t kDefaultSlidesWidth = 1850; -constexpr size_t kDefaultSlidesHeight = 1110; - // API is in development. Can be changed/removed without notice. class PeerConnectionE2EQualityTestFixture { public: - // The index of required capturing device in OS provided list of video - // devices. On Linux and Windows the list will be obtained via - // webrtc::VideoCaptureModule::DeviceInfo, on Mac OS via - // [RTCCameraVideoCapturer captureDevices]. - enum class CapturingDeviceIndex : size_t {}; - - // Contains parameters for screen share scrolling. - // - // If scrolling is enabled, then it will be done by putting sliding window - // on source video and moving this window from top left corner to the - // bottom right corner of the picture. - // - // In such case source dimensions must be greater or equal to the sliding - // window dimensions. So `source_width` and `source_height` are the dimensions - // of the source frame, while `VideoConfig::width` and `VideoConfig::height` - // are the dimensions of the sliding window. - // - // Because `source_width` and `source_height` are dimensions of the source - // frame, they have to be width and height of videos from - // `ScreenShareConfig::slides_yuv_file_names`. - // - // Because scrolling have to be done on single slide it also requires, that - // `duration` must be less or equal to - // `ScreenShareConfig::slide_change_interval`. - struct ScrollingParams { - ScrollingParams(TimeDelta duration, - size_t source_width, - size_t source_height) - : duration(duration), - source_width(source_width), - source_height(source_height) { - RTC_CHECK_GT(duration.ms(), 0); - } - - // Duration of scrolling. - TimeDelta duration; - // Width of source slides video. - size_t source_width; - // Height of source slides video. - size_t source_height; - }; - - // Contains screen share video stream properties. - struct ScreenShareConfig { - explicit ScreenShareConfig(TimeDelta slide_change_interval) - : slide_change_interval(slide_change_interval) { - RTC_CHECK_GT(slide_change_interval.ms(), 0); - } - - // Shows how long one slide should be presented on the screen during - // slide generation. - TimeDelta slide_change_interval; - // If true, slides will be generated programmatically. No scrolling params - // will be applied in such case. - bool generate_slides = false; - // If present scrolling will be applied. Please read extra requirement on - // `slides_yuv_file_names` for scrolling. - absl::optional scrolling_params; - // Contains list of yuv files with slides. - // - // If empty, default set of slides will be used. In such case - // `VideoConfig::width` must be equal to `kDefaultSlidesWidth` and - // `VideoConfig::height` must be equal to `kDefaultSlidesHeight` or if - // `scrolling_params` are specified, then `ScrollingParams::source_width` - // must be equal to `kDefaultSlidesWidth` and - // `ScrollingParams::source_height` must be equal to `kDefaultSlidesHeight`. - std::vector slides_yuv_file_names; - }; - - // 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 - // behavior in regular p2p call. Because of it there are such limitations: - // * if `target_spatial_index` is not equal to the highest spatial layer - // then no packet/frame drops are allowed. - // - // If there will be any drops, that will affect requested layer, then - // WebRTC SVC implementation will continue decoding only the highest - // available layer and won't restore lower layers, so analyzer won't - // receive required data which will cause wrong results or test failures. - struct VideoSimulcastConfig { - explicit VideoSimulcastConfig(int simulcast_streams_count) - : simulcast_streams_count(simulcast_streams_count) { - RTC_CHECK_GT(simulcast_streams_count, 1); - } - VideoSimulcastConfig(int simulcast_streams_count, int target_spatial_index) - : simulcast_streams_count(simulcast_streams_count), - target_spatial_index(target_spatial_index) { - RTC_CHECK_GT(simulcast_streams_count, 1); - RTC_CHECK_GE(target_spatial_index, 0); - RTC_CHECK_LT(target_spatial_index, simulcast_streams_count); - } - - // Specified amount of simulcast streams/SVC layers, depending on which - // encoder is used. - int simulcast_streams_count; - // Specifies spatial index of the video stream to analyze. - // There are 2 cases: - // 1. simulcast encoder is used: - // in such case `target_spatial_index` will specify the index of - // simulcast stream, that should be analyzed. Other streams will be - // dropped. - // 2. SVC encoder is used: - // in such case `target_spatial_index` will specify the top interesting - // spatial layer and all layers below, including target one will be - // processed. All layers above target one will be dropped. - // If not specified than whatever stream will be received will be analyzed. - // It requires Selective Forwarding Unit (SFU) to be configured in the - // network. - absl::optional target_spatial_index; - }; - - 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, - size_t width, - size_t height, - int32_t fps) - : width(width), - height(height), - fps(fps), - stream_label(std::move(stream_label)) {} - - // Video stream width. - size_t width; - // Video stream height. - 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; - // Will be set for current video track. If equals to kText or kDetailed - - // screencast in on. - absl::optional content_hint; - // If presented video will be transfered in simulcast/SVC mode depending on - // which encoder is used. - // - // Simulcast is supported only from 1st added peer. For VP8 simulcast only - // without RTX is supported so it will be automatically disabled for all - // simulcast tracks. For VP9 simulcast enables VP9 SVC mode and support RTX, - // 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; - // 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. - // It is useful when generator is used as input. - absl::optional input_dump_file_name; - // Used only if `input_dump_file_name` is set. Specifies the module for the - // video frames to be dumped. Modulo equals X means every Xth frame will be - // written to the dump file. The value must be greater than 0. - int input_dump_sampling_modulo = 1; - // If specified this file will be used as output on the receiver side for - // this stream. - // - // If multiple output streams will be produced by this stream (e.g. when the - // stream represented by this `VideoConfig` is received by more than one - // peer), output files will be appended with receiver names. If the second - // and other receivers will be added in the middle of the call after the - // first frame for this stream has been already written to the output file, - // then only dumps for newly added peers will be appended with receiver - // name, the dump for the first receiver will have name equal to the - // specified one. For example: - // * If we have peers A and B and A has `VideoConfig` V_a with - // V_a.output_dump_file_name = "/foo/a_output.yuv", then the stream - // related to V_a will be written into "/foo/a_output.yuv". - // * If we have peers A, B and C and A has `VideoConfig` V_a with - // V_a.output_dump_file_name = "/foo/a_output.yuv", then the stream - // related to V_a will be written for peer B into "/foo/a_output.yuv.B" - // and for peer C into "/foo/a_output.yuv.C" - // * If we have peers A and B and A has `VideoConfig` V_a with - // V_a.output_dump_file_name = "/foo/a_output.yuv", then if after B - // received the first frame related to V_a peer C joined the call, then - // the stream related to V_a will be written for peer B into - // "/foo/a_output.yuv" and for peer C into "/foo/a_output.yuv.C" - // - // The produced files contains what was rendered for this video stream on - // receiver side. - absl::optional output_dump_file_name; - // Used only if `output_dump_file_name` is set. Specifies the module for the - // video frames to be dumped. Modulo equals X means every Xth frame will be - // written to the dump file. The value must be greater than 0. - int output_dump_sampling_modulo = 1; - // If true will display input and output video on the user's screen. - bool show_on_screen = false; - // If specified, determines a sync group to which this video stream belongs. - // According to bugs.webrtc.org/4762 WebRTC supports synchronization only - // for pair of single audio and single video stream. - absl::optional sync_group; - }; - - // Contains properties for audio in the call. - struct AudioConfig { - enum Mode { - kGenerated, - kFile, - }; - - AudioConfig() = default; - explicit AudioConfig(std::string stream_label) - : stream_label(std::move(stream_label)) {} - - // Have to be unique among all specified configs for all peers in the call. - // Will be auto generated if omitted. - absl::optional stream_label; - Mode mode = kGenerated; - // Have to be specified only if mode = kFile - absl::optional input_file_name; - // If specified the input stream will be also copied to specified file. - absl::optional input_dump_file_name; - // If specified the output stream will be copied to specified file. - absl::optional output_dump_file_name; - - // Audio options to use. - cricket::AudioOptions audio_options; - // Sampling frequency of input audio data (from file or generated). - int sampling_frequency_in_hz = 48000; - // If specified, determines a sync group to which this audio stream belongs. - // According to bugs.webrtc.org/4762 WebRTC supports synchronization only - // for pair of single audio and single video stream. - absl::optional sync_group; - }; - - struct VideoCodecConfig { - explicit VideoCodecConfig(std::string name) - : name(std::move(name)), required_params() {} - VideoCodecConfig(std::string name, - std::map required_params) - : name(std::move(name)), required_params(std::move(required_params)) {} - // Next two fields are used to specify concrete video codec, that should be - // used in the test. Video code will be negotiated in SDP during offer/ - // answer exchange. - // Video codec name. You can find valid names in - // media/base/media_constants.h - std::string name = cricket::kVp8CodecName; - // Map of parameters, that have to be specified on SDP codec. Each parameter - // is described by key and value. Codec parameters will match the specified - // map if and only if for each key from `required_params` there will be - // a parameter with name equal to this key and parameter value will be equal - // to the value from `required_params` for this key. - // If empty then only name will be used to match the codec. - 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: - virtual ~PeerConfigurer() = default; - - // Sets peer name that will be used to report metrics related to this peer. - // If not set, some default name will be assigned. All names have to be - // unique. - virtual PeerConfigurer* SetName(absl::string_view name) = 0; - - // The parameters of the following 9 methods will be passed to the - // PeerConnectionFactoryInterface implementation that will be created for - // this peer. - virtual PeerConfigurer* SetTaskQueueFactory( - std::unique_ptr task_queue_factory) = 0; - virtual PeerConfigurer* SetCallFactory( - std::unique_ptr call_factory) = 0; - virtual PeerConfigurer* SetEventLogFactory( - std::unique_ptr event_log_factory) = 0; - virtual PeerConfigurer* SetFecControllerFactory( - std::unique_ptr - fec_controller_factory) = 0; - virtual PeerConfigurer* SetNetworkControllerFactory( - std::unique_ptr - network_controller_factory) = 0; - virtual PeerConfigurer* SetVideoEncoderFactory( - std::unique_ptr video_encoder_factory) = 0; - virtual PeerConfigurer* SetVideoDecoderFactory( - std::unique_ptr video_decoder_factory) = 0; - // 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 - // peer. - virtual PeerConfigurer* SetAsyncResolverFactory( - std::unique_ptr - async_resolver_factory) = 0; - virtual PeerConfigurer* SetRTCCertificateGenerator( - std::unique_ptr - cert_generator) = 0; - virtual PeerConfigurer* SetSSLCertificateVerifier( - 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. - virtual PeerConfigurer* AddVideoConfig(VideoConfig config) = 0; - // Add new video stream to the call that will be sent from this peer with - // provided own implementation of video frames generator. - virtual PeerConfigurer* AddVideoConfig( - VideoConfig config, - std::unique_ptr generator) = 0; - // Add new video stream to the call that will be sent from this peer. - // Capturing device with specified index will be used to get input video. - 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`. - // Codecs have to be available in codecs list provided by peer connection to - // be negotiated. If some of specified codecs won't be found, the test will - // crash. - virtual PeerConfigurer* SetVideoCodecs( - std::vector video_codecs) = 0; - // Set the audio stream for the call from this peer. If this method won't - // be invoked, this peer will send no audio. - virtual PeerConfigurer* SetAudioConfig(AudioConfig config) = 0; - - // Set if ULP FEC should be used or not. False by default. - virtual PeerConfigurer* SetUseUlpFEC(bool value) = 0; - // Set if Flex FEC should be used or not. False by default. - // Client also must enable `enable_flex_fec_support` in the `RunParams` to - // be able to use this feature. - virtual PeerConfigurer* SetUseFlexFEC(bool value) = 0; - // Specifies how much video encoder target bitrate should be different than - // target bitrate, provided by WebRTC stack. Must be greater than 0. Can be - // used to emulate overshooting of video encoders. This multiplier will - // be applied for all video encoder on both sides for all layers. Bitrate - // estimated by WebRTC stack will be multiplied by this multiplier and then - // provided into VideoEncoder::SetRates(...). 1.0 by default. - virtual PeerConfigurer* SetVideoEncoderBitrateMultiplier( - double multiplier) = 0; - - // If is set, an RTCEventLog will be saved in that location and it will be - // available for further analysis. - virtual PeerConfigurer* SetRtcEventLogPath(std::string path) = 0; - // If is set, an AEC dump will be saved in that location and it will be - // available for further analysis. - 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( - BitrateSettings bitrate_settings) = 0; - }; - - // Contains configuration for echo emulator. - struct EchoEmulationConfig { - // Delay which represents the echo path delay, i.e. how soon rendered signal - // should reach capturer. - TimeDelta echo_delay = TimeDelta::Millis(50); - }; - - // Contains parameters, that describe how long framework should run quality - // test. - struct RunParams { - explicit RunParams(TimeDelta run_duration) : run_duration(run_duration) {} - - // Specifies how long the test should be run. This time shows how long - // the media should flow after connection was established and before - // it will be shut downed. - TimeDelta run_duration; - - // If set to true peers will be able to use Flex FEC, otherwise they won't - // be able to negotiate it even if it's enabled on per peer level. - bool enable_flex_fec_support = false; - // If true will set conference mode in SDP media section for all video - // tracks for all peers. - bool use_conference_mode = false; - // If specified echo emulation will be done, by mixing the render audio into - // the capture signal. In such case input signal will be reduced by half to - // avoid saturation or compression in the echo path simulation. - absl::optional echo_emulation_config; - }; - // Represent an entity that will report quality metrics after test. class QualityMetricsReporter : public StatsObserverInterface { public: @@ -627,9 +118,7 @@ class PeerConnectionE2EQualityTestFixture { // `network_dependencies` are used to provide networking for peer's peer // connection. Members must be non-null. // `configurer` function will be used to configure peer in the call. - virtual PeerHandle* AddPeer( - const PeerNetworkDependencies& network_dependencies, - rtc::FunctionView configurer) = 0; + virtual PeerHandle* AddPeer(std::unique_ptr configurer) = 0; // Runs the media quality test, which includes setting up the call with // configured participants, running it according to provided `run_params` and diff --git a/api/test/peerconnection_quality_test_fixture_unittest.cc b/api/test/peerconnection_quality_test_fixture_unittest.cc index c0e739cc23..26ae8cf98f 100644 --- a/api/test/peerconnection_quality_test_fixture_unittest.cc +++ b/api/test/peerconnection_quality_test_fixture_unittest.cc @@ -13,21 +13,20 @@ #include #include "absl/types/optional.h" +#include "api/test/pclf/media_configuration.h" +#include "api/test/video/video_frame_writer.h" #include "rtc_base/gunit.h" #include "test/gmock.h" +#include "test/testsupport/file_utils.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; +using ::testing::Eq; -TEST(PclfVideoSubscription, MaxFromSenderSpecEqualIndependentOfOtherFields) { +TEST(PclfVideoSubscriptionTest, + MaxFromSenderSpecEqualIndependentOfOtherFields) { VideoResolution r1(VideoResolution::Spec::kMaxFromSender); r1.set_width(1); r1.set_height(2); @@ -39,7 +38,7 @@ TEST(PclfVideoSubscription, MaxFromSenderSpecEqualIndependentOfOtherFields) { EXPECT_EQ(r1, r2); } -TEST(PclfVideoSubscription, WhenSpecIsNotSetFieldsAreCompared) { +TEST(PclfVideoSubscriptionTest, WhenSpecIsNotSetFieldsAreCompared) { VideoResolution test_resolution(/*width=*/1, /*height=*/2, /*fps=*/3); VideoResolution equal_resolution(/*width=*/1, /*height=*/2, @@ -57,13 +56,13 @@ TEST(PclfVideoSubscription, WhenSpecIsNotSetFieldsAreCompared) { EXPECT_NE(test_resolution, different_fps); } -TEST(PclfVideoSubscription, GetMaxResolutionForEmptyReturnsNullopt) { +TEST(PclfVideoSubscriptionTest, GetMaxResolutionForEmptyReturnsNullopt) { absl::optional resolution = VideoSubscription::GetMaxResolution(std::vector{}); ASSERT_FALSE(resolution.has_value()); } -TEST(PclfVideoSubscription, GetMaxResolutionSelectMaxForEachDimention) { +TEST(PclfVideoSubscriptionTest, 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); @@ -77,6 +76,67 @@ TEST(PclfVideoSubscription, GetMaxResolutionSelectMaxForEachDimention) { EXPECT_EQ(resolution->fps(), 10); } +struct TestVideoFrameWriter : public test::VideoFrameWriter { + public: + TestVideoFrameWriter(absl::string_view file_name_prefix, + const VideoResolution& resolution) + : file_name_prefix(file_name_prefix), resolution(resolution) {} + + bool WriteFrame(const VideoFrame& frame) override { return true; } + + void Close() override {} + + std::string file_name_prefix; + VideoResolution resolution; +}; + +TEST(VideoDumpOptionsTest, InputVideoWriterHasCorrectFileName) { + VideoResolution resolution(/*width=*/1280, /*height=*/720, /*fps=*/30); + + TestVideoFrameWriter* writer = nullptr; + VideoDumpOptions options("foo", /*sampling_modulo=*/1, + /*export_frame_ids=*/false, + /*video_frame_writer_factory=*/ + [&](absl::string_view file_name_prefix, + const VideoResolution& resolution) { + auto out = std::make_unique( + file_name_prefix, resolution); + writer = out.get(); + return out; + }); + std::unique_ptr created_writer = + options.CreateInputDumpVideoFrameWriter("alice-video", resolution); + + ASSERT_TRUE(writer != nullptr); + ASSERT_THAT(writer->file_name_prefix, + Eq(test::JoinFilename("foo", "alice-video_1280x720_30"))); + ASSERT_THAT(writer->resolution, Eq(resolution)); +} + +TEST(VideoDumpOptionsTest, OutputVideoWriterHasCorrectFileName) { + VideoResolution resolution(/*width=*/1280, /*height=*/720, /*fps=*/30); + + TestVideoFrameWriter* writer = nullptr; + VideoDumpOptions options("foo", /*sampling_modulo=*/1, + /*export_frame_ids=*/false, + /*video_frame_writer_factory=*/ + [&](absl::string_view file_name_prefix, + const VideoResolution& resolution) { + auto out = std::make_unique( + file_name_prefix, resolution); + writer = out.get(); + return out; + }); + std::unique_ptr created_writer = + options.CreateOutputDumpVideoFrameWriter("alice-video", "bob", + resolution); + + ASSERT_TRUE(writer != nullptr); + ASSERT_THAT(writer->file_name_prefix, + Eq(test::JoinFilename("foo", "alice-video_bob_1280x720_30"))); + ASSERT_THAT(writer->resolution, Eq(resolution)); +} + } // namespace } // namespace webrtc_pc_e2e } // namespace webrtc diff --git a/api/test/simulated_network.h b/api/test/simulated_network.h index fcac51f4ea..04c5517c8d 100644 --- a/api/test/simulated_network.h +++ b/api/test/simulated_network.h @@ -38,6 +38,12 @@ struct PacketDeliveryInfo { static constexpr int kNotReceived = -1; PacketDeliveryInfo(PacketInFlightInfo source, int64_t receive_time_us) : receive_time_us(receive_time_us), packet_id(source.packet_id) {} + + bool operator==(const PacketDeliveryInfo& other) const { + return receive_time_us == other.receive_time_us && + packet_id == other.packet_id; + } + int64_t receive_time_us; uint64_t packet_id; }; @@ -62,18 +68,52 @@ struct BuiltInNetworkBehaviorConfig { int avg_burst_loss_length = -1; // Additional bytes to add to packet size. int packet_overhead = 0; - // Enable CoDel active queue management. - bool codel_active_queue_management = false; }; +// Interface that represents a Network behaviour. +// +// It is clients of this interface responsibility to enqueue and dequeue +// packets (based on the estimated delivery time expressed by +// NextDeliveryTimeUs). +// +// To enqueue packets, call EnqueuePacket: +// EXPECT_TRUE(network.EnqueuePacket( +// PacketInFlightInfo(/*size=*/1, /*send_time_us=*/0, /*packet_id=*/1))); +// +// To know when to call DequeueDeliverablePackets to pull packets out of the +// network, call NextDeliveryTimeUs and schedule a task to invoke +// DequeueDeliverablePackets (if not already scheduled). +// +// DequeueDeliverablePackets will return a vector of delivered packets, but this +// vector can be empty in case of extra delay. In such case, make sure to invoke +// NextDeliveryTimeUs and schedule a task to call DequeueDeliverablePackets for +// the next estimated delivery of packets. +// +// std::vector delivered_packets = +// network.DequeueDeliverablePackets(/*receive_time_us=*/1000000); class NetworkBehaviorInterface { public: + // Enqueues a packet in the network and returns true if the action was + // successful, false otherwise (for example, because the network capacity has + // been saturated). If the return value is false, the packet should be + // considered as dropped and it will not be returned by future calls + // to DequeueDeliverablePackets. + // Packets enqueued will exit the network when DequeueDeliverablePackets is + // called and enough time has passed (see NextDeliveryTimeUs). virtual bool EnqueuePacket(PacketInFlightInfo packet_info) = 0; // Retrieves all packets that should be delivered by the given receive time. + // Not all the packets in the returned std::vector are actually delivered. + // In order to know the state of each packet it is necessary to check the + // `receive_time_us` field of each packet. If that is set to + // PacketDeliveryInfo::kNotReceived then the packet is considered lost in the + // network. virtual std::vector DequeueDeliverablePackets( int64_t receive_time_us) = 0; // Returns time in microseconds when caller should call - // DequeueDeliverablePackets to get next set of packets to deliver. + // DequeueDeliverablePackets to get the next set of delivered packets. It is + // possible that no packet will be delivered by that time (e.g. in case of + // random extra delay), in such case this method should be called again to get + // the updated estimated delivery time. virtual absl::optional NextDeliveryTimeUs() const = 0; virtual ~NetworkBehaviorInterface() = default; }; @@ -83,10 +123,14 @@ class NetworkBehaviorInterface { // capacity introduced delay. class SimulatedNetworkInterface : public NetworkBehaviorInterface { public: - // Sets a new configuration. This won't affect packets already in the pipe. + // Sets a new configuration. virtual void SetConfig(const BuiltInNetworkBehaviorConfig& config) = 0; virtual void UpdateConfig( std::function config_modifier) = 0; + // Pauses the network until `until_us`. This affects both delivery (calling + // DequeueDeliverablePackets before `until_us` results in an empty std::vector + // of packets) and capacity (the network is paused, so packets are not + // flowing and they will restart flowing at `until_us`). virtual void PauseTransmissionUntil(int64_t until_us) = 0; }; diff --git a/api/test/simulcast_test_fixture.h b/api/test/simulcast_test_fixture.h index cd470703c3..c7130d2909 100644 --- a/api/test/simulcast_test_fixture.h +++ b/api/test/simulcast_test_fixture.h @@ -19,6 +19,7 @@ class SimulcastTestFixture { virtual ~SimulcastTestFixture() = default; virtual void TestKeyFrameRequestsOnAllStreams() = 0; + virtual void TestKeyFrameRequestsOnSpecificStreams() = 0; virtual void TestPaddingAllStreams() = 0; virtual void TestPaddingTwoStreams() = 0; virtual void TestPaddingTwoStreamsOneMaxedOut() = 0; diff --git a/api/test/time_controller.h b/api/test/time_controller.h index 17aa0db80f..121f65cea9 100644 --- a/api/test/time_controller.h +++ b/api/test/time_controller.h @@ -17,7 +17,6 @@ #include "api/task_queue/task_queue_factory.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" -#include "modules/utility/include/process_thread.h" #include "rtc_base/synchronization/yield_policy.h" #include "rtc_base/thread.h" #include "system_wrappers/include/clock.h" @@ -41,9 +40,6 @@ class TimeController { // is destroyed. std::unique_ptr CreateTaskQueueFactory(); - // Creates a process thread. - virtual std::unique_ptr CreateProcessThread( - const char* thread_name) = 0; // Creates an rtc::Thread instance. If `socket_server` is nullptr, a default // noop socket server is created. // Returned thread is not null and started. diff --git a/api/test/track_id_stream_info_map.h b/api/test/track_id_stream_info_map.h index 0f8e43e20e..b016de57a9 100644 --- a/api/test/track_id_stream_info_map.h +++ b/api/test/track_id_stream_info_map.h @@ -11,6 +11,8 @@ #ifndef API_TEST_TRACK_ID_STREAM_INFO_MAP_H_ #define API_TEST_TRACK_ID_STREAM_INFO_MAP_H_ +#include + #include "absl/strings/string_view.h" namespace webrtc { @@ -20,19 +22,19 @@ namespace webrtc_pc_e2e { // are useful to associate stats reports track_ids to the remote stream info. class TrackIdStreamInfoMap { public: + struct StreamInfo { + std::string receiver_peer; + std::string stream_label; + std::string sync_group; + }; + virtual ~TrackIdStreamInfoMap() = default; // These methods must be called on the same thread where // StatsObserverInterface::OnStatsReports is invoked. - // Returns a reference to a stream label owned by the TrackIdStreamInfoMap. - // Precondition: `track_id` must be already mapped to stream label. - virtual absl::string_view GetStreamLabelFromTrackId( - absl::string_view track_id) const = 0; - - // Returns a reference to a sync group name owned by the TrackIdStreamInfoMap. - // Precondition: `track_id` must be already mapped to sync group. - virtual absl::string_view GetSyncGroupLabelFromTrackId( + // Precondition: `track_id` must be already mapped to stream info. + virtual StreamInfo GetStreamInfoFromTrackId( absl::string_view track_id) const = 0; }; diff --git a/api/test/video/BUILD.gn b/api/test/video/BUILD.gn index 4ebb0c9167..d24ffa5fa6 100644 --- a/api/test/video/BUILD.gn +++ b/api/test/video/BUILD.gn @@ -21,3 +21,11 @@ rtc_library("function_video_factory") { "../../video_codecs:video_codecs_api", ] } + +rtc_library("video_frame_writer") { + visibility = [ "*" ] + testonly = true + public = [ "video_frame_writer.h" ] + + deps = [ "../../video:video_frame" ] +} diff --git a/api/test/video/function_video_decoder_factory.h b/api/test/video/function_video_decoder_factory.h index 86abdd0746..2145c71bff 100644 --- a/api/test/video/function_video_decoder_factory.h +++ b/api/test/video/function_video_decoder_factory.h @@ -17,6 +17,7 @@ #include #include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_decoder.h" #include "api/video_codecs/video_decoder_factory.h" #include "rtc_base/checks.h" diff --git a/api/test/video/function_video_encoder_factory.h b/api/test/video/function_video_encoder_factory.h index 9ae9719916..98ece2bc94 100644 --- a/api/test/video/function_video_encoder_factory.h +++ b/api/test/video/function_video_encoder_factory.h @@ -17,6 +17,7 @@ #include #include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" #include "rtc_base/checks.h" diff --git a/api/test/video/video_frame_writer.h b/api/test/video/video_frame_writer.h new file mode 100644 index 0000000000..ac72534890 --- /dev/null +++ b/api/test/video/video_frame_writer.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_TEST_VIDEO_VIDEO_FRAME_WRITER_H_ +#define API_TEST_VIDEO_VIDEO_FRAME_WRITER_H_ + +#include "api/video/video_frame.h" + +namespace webrtc { +namespace test { + +class VideoFrameWriter { + public: + virtual ~VideoFrameWriter() = default; + + // Writes `VideoFrame` and returns true if operation was successful, false + // otherwise. + // + // Calling `WriteFrame` after `Close` is not allowed. + virtual bool WriteFrame(const VideoFrame& frame) = 0; + + // Closes writer and cleans up all resources. No invocations to `WriteFrame` + // are allowed after `Close` was invoked. + virtual void Close() = 0; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_VIDEO_VIDEO_FRAME_WRITER_H_ diff --git a/api/test/video_codec_stats.h b/api/test/video_codec_stats.h new file mode 100644 index 0000000000..b1dfee8b75 --- /dev/null +++ b/api/test/video_codec_stats.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 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_VIDEO_CODEC_STATS_H_ +#define API_TEST_VIDEO_CODEC_STATS_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/numerics/samples_stats_counter.h" +#include "api/test/metrics/metric.h" +#include "api/test/metrics/metrics_logger.h" +#include "api/units/data_rate.h" +#include "api/units/frequency.h" + +namespace webrtc { +namespace test { + +// Interface for encoded and/or decoded video frame and stream statistics. +class VideoCodecStats { + public: + // Filter for slicing frames. + struct Filter { + absl::optional first_frame; + absl::optional last_frame; + absl::optional spatial_idx; + absl::optional temporal_idx; + }; + + struct Frame { + int frame_num = 0; + uint32_t timestamp_rtp = 0; + + int spatial_idx = 0; + int temporal_idx = 0; + + int width = 0; + int height = 0; + int size_bytes = 0; + bool keyframe = false; + absl::optional qp = absl::nullopt; + absl::optional base_spatial_idx = absl::nullopt; + + Timestamp encode_start = Timestamp::Zero(); + TimeDelta encode_time = TimeDelta::Zero(); + Timestamp decode_start = Timestamp::Zero(); + TimeDelta decode_time = TimeDelta::Zero(); + + struct Psnr { + double y = 0.0; + double u = 0.0; + double v = 0.0; + }; + absl::optional psnr = absl::nullopt; + + bool encoded = false; + bool decoded = false; + }; + + struct Stream { + int num_frames = 0; + int num_keyframes = 0; + + SamplesStatsCounter width; + SamplesStatsCounter height; + SamplesStatsCounter size_bytes; + SamplesStatsCounter qp; + + SamplesStatsCounter encode_time_us; + SamplesStatsCounter decode_time_us; + + DataRate bitrate = DataRate::Zero(); + Frequency framerate = Frequency::Zero(); + int bitrate_mismatch_pct = 0; + int framerate_mismatch_pct = 0; + SamplesStatsCounter transmission_time_us; + + struct Psnr { + SamplesStatsCounter y; + SamplesStatsCounter u; + SamplesStatsCounter v; + } psnr; + }; + + virtual ~VideoCodecStats() = default; + + // Returns frames from interval, spatial and temporal layer specified by given + // `filter`. + virtual std::vector Slice( + absl::optional filter = absl::nullopt) const = 0; + + // Returns video statistics aggregated for given `frames`. If `bitrate` is + // provided, also performs rate control analysis. If `framerate` is provided, + // also calculates frame rate mismatch. + virtual Stream Aggregate( + const std::vector& frames, + absl::optional bitrate = absl::nullopt, + absl::optional framerate = absl::nullopt) const = 0; + + // Logs `Stream` metrics to provided `MetricsLogger`. + virtual void LogMetrics(MetricsLogger* logger, + const Stream& stream, + std::string test_case_name) const = 0; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_VIDEO_CODEC_STATS_H_ diff --git a/api/test/video_codec_tester.h b/api/test/video_codec_tester.h new file mode 100644 index 0000000000..149f0de611 --- /dev/null +++ b/api/test/video_codec_tester.h @@ -0,0 +1,139 @@ +/* + * 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_TEST_VIDEO_CODEC_TESTER_H_ +#define API_TEST_VIDEO_CODEC_TESTER_H_ + +#include + +#include "absl/functional/any_invocable.h" +#include "absl/types/optional.h" +#include "api/test/video_codec_stats.h" +#include "api/video/encoded_image.h" +#include "api/video/resolution.h" +#include "api/video/video_frame.h" + +namespace webrtc { +namespace test { + +// Interface for a video codec tester. The interface provides minimalistic set +// of data structures that enables implementation of decode-only, encode-only +// and encode-decode tests. +class VideoCodecTester { + public: + // Pacing settings for codec input. + struct PacingSettings { + enum PacingMode { + // Pacing is not used. Frames are sent to codec back-to-back. + kNoPacing, + // Pace with the rate equal to the target video frame rate. Pacing time is + // derived from RTP timestamp. + kRealTime, + // Pace with the explicitly provided rate. + kConstantRate, + }; + PacingMode mode = PacingMode::kNoPacing; + // Pacing rate for `kConstantRate` mode. + Frequency constant_rate = Frequency::Zero(); + }; + + struct DecoderSettings { + PacingSettings pacing; + }; + + struct EncoderSettings { + PacingSettings pacing; + }; + + virtual ~VideoCodecTester() = default; + + // Interface for a raw video frames source. + class RawVideoSource { + public: + virtual ~RawVideoSource() = default; + + // Returns next frame. If no more frames to pull, returns `absl::nullopt`. + // For analysis and pacing purposes, frame must have RTP timestamp set. The + // timestamp must represent the target video frame rate and be unique. + virtual absl::optional PullFrame() = 0; + + // Returns early pulled frame with RTP timestamp equal to `timestamp_rtp`. + virtual VideoFrame GetFrame(uint32_t timestamp_rtp, + Resolution resolution) = 0; + }; + + // Interface for a coded video frames source. + class CodedVideoSource { + public: + virtual ~CodedVideoSource() = default; + + // Returns next frame. If no more frames to pull, returns `absl::nullopt`. + // For analysis and pacing purposes, frame must have RTP timestamp set. The + // timestamp must represent the target video frame rate and be unique. + virtual absl::optional PullFrame() = 0; + }; + + // Interface for a video encoder. + class Encoder { + public: + using EncodeCallback = + absl::AnyInvocable; + + virtual ~Encoder() = default; + + virtual void Encode(const VideoFrame& frame, EncodeCallback callback) = 0; + + virtual void Flush() = 0; + }; + + // Interface for a video decoder. + class Decoder { + public: + using DecodeCallback = + absl::AnyInvocable; + + virtual ~Decoder() = default; + + virtual void Decode(const EncodedImage& frame, DecodeCallback callback) = 0; + + virtual void Flush() = 0; + }; + + // Pulls coded video frames from `video_source` and passes them to `decoder`. + // Returns `VideoCodecTestStats` object that contains collected per-frame + // metrics. + virtual std::unique_ptr RunDecodeTest( + CodedVideoSource* video_source, + Decoder* decoder, + const DecoderSettings& decoder_settings) = 0; + + // Pulls raw video frames from `video_source` and passes them to `encoder`. + // Returns `VideoCodecTestStats` object that contains collected per-frame + // metrics. + virtual std::unique_ptr RunEncodeTest( + RawVideoSource* video_source, + Encoder* encoder, + const EncoderSettings& encoder_settings) = 0; + + // Pulls raw video frames from `video_source`, passes them to `encoder` and + // then passes encoded frames to `decoder`. Returns `VideoCodecTestStats` + // object that contains collected per-frame metrics. + virtual std::unique_ptr RunEncodeDecodeTest( + RawVideoSource* video_source, + Encoder* encoder, + Decoder* decoder, + const EncoderSettings& encoder_settings, + const DecoderSettings& decoder_settings) = 0; +}; + +} // namespace test +} // namespace webrtc + +#endif // API_TEST_VIDEO_CODEC_TESTER_H_ diff --git a/api/test/video_quality_analyzer_interface.h b/api/test/video_quality_analyzer_interface.h index d27c9ea015..d35be8ca1a 100644 --- a/api/test/video_quality_analyzer_interface.h +++ b/api/test/video_quality_analyzer_interface.h @@ -62,6 +62,8 @@ class VideoQualityAnalyzerInterface // https://crbug.com/webrtc/11443: improve stats API to make available // there. uint32_t target_encode_bitrate = 0; + // Encoder quantizer value. + int qp = -1; }; // Contains extra statistic provided by video decoder. struct DecoderStats { @@ -101,7 +103,8 @@ class VideoQualityAnalyzerInterface virtual void OnFrameEncoded(absl::string_view peer_name, uint16_t frame_id, const EncodedImage& encoded_image, - const EncoderStats& stats) {} + const EncoderStats& stats, + bool discarded) {} // Will be called for each frame dropped by encoder. // `peer_name` is name of the peer on which side frame drop was detected. virtual void OnFrameDropped(absl::string_view peer_name, @@ -133,7 +136,8 @@ class VideoQualityAnalyzerInterface // `peer_name` is name of the peer on which side error acquired. virtual void OnDecoderError(absl::string_view peer_name, uint16_t frame_id, - int32_t error_code) {} + int32_t error_code, + const DecoderStats& stats) {} // Will be called every time new stats reports are available for the // Peer Connection identified by `pc_label`. void OnStatsReports( @@ -142,6 +146,9 @@ class VideoQualityAnalyzerInterface // Will be called before test adds new participant in the middle of a call. virtual void RegisterParticipantInCall(absl::string_view peer_name) {} + // Will be called after test removed existing participant in the middle of the + // call. + virtual void UnregisterParticipantInCall(absl::string_view peer_name) {} // Tells analyzer that analysis complete and it should calculate final // statistics. @@ -153,12 +160,6 @@ class VideoQualityAnalyzerInterface virtual std::string GetStreamLabel(uint16_t frame_id) = 0; }; -namespace webrtc_pc_e2e { - -// Temporary alias to make downstream projects able to migrate. -using VideoQualityAnalyzerInterface = ::webrtc::VideoQualityAnalyzerInterface; - -} // namespace webrtc_pc_e2e } // namespace webrtc #endif // API_TEST_VIDEO_QUALITY_ANALYZER_INTERFACE_H_ diff --git a/api/test/video_quality_test_fixture.h b/api/test/video_quality_test_fixture.h index 08ae12b816..b45faef286 100644 --- a/api/test/video_quality_test_fixture.h +++ b/api/test/video_quality_test_fixture.h @@ -24,8 +24,8 @@ #include "api/transport/network_control.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_decoder_factory.h" -#include "api/video_codecs/video_encoder_config.h" #include "api/video_codecs/video_encoder_factory.h" +#include "video/config/video_encoder_config.h" namespace webrtc { @@ -37,6 +37,7 @@ class VideoQualityTestFixtureInterface { struct CallConfig { bool send_side_bwe = false; bool generic_descriptor = false; + bool dependency_descriptor = false; BitrateConstraints call_bitrate_config; int num_thumbnails = 0; // Indicates if secondary_(video|ss|screenshare) structures are used. diff --git a/api/test/videocodec_test_fixture.h b/api/test/videocodec_test_fixture.h index dbf20993e2..8e66f72b91 100644 --- a/api/test/videocodec_test_fixture.h +++ b/api/test/videocodec_test_fixture.h @@ -54,6 +54,7 @@ struct BitstreamThresholds { }; // NOTE: This class is still under development and may change without notice. +// TODO(webrtc:14852): Deprecated in favor VideoCodecTester. class VideoCodecTestFixture { public: class EncodedFrameChecker { diff --git a/api/test/videocodec_test_stats.h b/api/test/videocodec_test_stats.h index a05985a665..d620d31f12 100644 --- a/api/test/videocodec_test_stats.h +++ b/api/test/videocodec_test_stats.h @@ -18,12 +18,16 @@ #include #include +#include "absl/types/optional.h" +#include "api/units/data_rate.h" +#include "api/units/frequency.h" #include "api/video/video_frame_type.h" namespace webrtc { namespace test { // Statistics for a sequence of processed frames. This class is not thread safe. +// TODO(webrtc:14852): Deprecated in favor VideoCodecStats. class VideoCodecTestStats { public: // Statistics for one processed frame. @@ -135,11 +139,16 @@ class VideoCodecTestStats { virtual ~VideoCodecTestStats() = default; - virtual std::vector GetFrameStatistics() = 0; + virtual std::vector GetFrameStatistics() const = 0; virtual std::vector SliceAndCalcLayerVideoStatistic( size_t first_frame_num, size_t last_frame_num) = 0; + + virtual VideoStatistics CalcVideoStatistic(size_t first_frame, + size_t last_frame, + DataRate target_bitrate, + Frequency target_framerate) = 0; }; } // namespace test diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn index 44d4b117b5..08f3d6d1d0 100644 --- a/api/transport/BUILD.gn +++ b/api/transport/BUILD.gn @@ -52,13 +52,12 @@ rtc_library("field_trial_based_config") { "field_trial_based_config.h", ] deps = [ - "../../api:field_trials_view", + "../../api:field_trials_registry", "../../system_wrappers:field_trial", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } -# TODO(nisse): Rename? rtc_source_set("datagram_transport_interface") { visibility = [ "*" ] sources = [ "data_channel_transport_interface.h" ] @@ -102,10 +101,12 @@ rtc_source_set("stun_types") { "../../rtc_base:byte_buffer", "../../rtc_base:byte_order", "../../rtc_base:checks", + "../../rtc_base:crc32", "../../rtc_base:ip_address", "../../rtc_base:logging", - "../../rtc_base:rtc_base", "../../rtc_base:socket_address", + "../../rtc_base:ssl", + "../../system_wrappers:metrics", ] absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] } @@ -141,11 +142,11 @@ if (rtc_include_tests) { sources = [ "stun_unittest.cc" ] deps = [ ":stun_types", - "../../rtc_base", "../../rtc_base:byte_buffer", "../../rtc_base:byte_order", "../../rtc_base:macromagic", "../../rtc_base:socket_address", + "../../system_wrappers:metrics", "../../test:test_support", "//testing/gtest", ] diff --git a/api/transport/field_trial_based_config.cc b/api/transport/field_trial_based_config.cc index 4a3a179240..0cef30f054 100644 --- a/api/transport/field_trial_based_config.cc +++ b/api/transport/field_trial_based_config.cc @@ -12,7 +12,7 @@ #include "system_wrappers/include/field_trial.h" namespace webrtc { -std::string FieldTrialBasedConfig::Lookup(absl::string_view key) const { +std::string FieldTrialBasedConfig::GetValue(absl::string_view key) const { return webrtc::field_trial::FindFullName(std::string(key)); } } // namespace webrtc diff --git a/api/transport/field_trial_based_config.h b/api/transport/field_trial_based_config.h index f0063ff95e..d47140e579 100644 --- a/api/transport/field_trial_based_config.h +++ b/api/transport/field_trial_based_config.h @@ -13,13 +13,13 @@ #include #include "absl/strings/string_view.h" -#include "api/field_trials_view.h" +#include "api/field_trials_registry.h" namespace webrtc { // Implementation using the field trial API fo the key value lookup. -class FieldTrialBasedConfig : public FieldTrialsView { - public: - std::string Lookup(absl::string_view key) const override; +class FieldTrialBasedConfig : public FieldTrialsRegistry { + private: + std::string GetValue(absl::string_view key) const override; }; } // namespace webrtc diff --git a/api/transport/rtp/BUILD.gn b/api/transport/rtp/BUILD.gn index 26036c7f32..205bbcc988 100644 --- a/api/transport/rtp/BUILD.gn +++ b/api/transport/rtp/BUILD.gn @@ -13,6 +13,7 @@ rtc_source_set("rtp_source") { sources = [ "rtp_source.h" ] deps = [ "../../../api:rtp_headers", + "../../../api/units:time_delta", "../../../rtc_base:checks", ] absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] diff --git a/api/transport/rtp/rtp_source.h b/api/transport/rtp/rtp_source.h index 8c543cac0c..e51dcd70b6 100644 --- a/api/transport/rtp/rtp_source.h +++ b/api/transport/rtp/rtp_source.h @@ -15,6 +15,7 @@ #include "absl/types/optional.h" #include "api/rtp_headers.h" +#include "api/units/time_delta.h" #include "rtc_base/checks.h" namespace webrtc { @@ -28,24 +29,21 @@ class RtpSource { public: struct Extensions { absl::optional audio_level; + + // Fields from the Absolute Capture Time header extension: + // http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time absl::optional absolute_capture_time; + + // Clock offset between the local clock and the capturer's clock. + // Do not confuse with `AbsoluteCaptureTime::estimated_capture_clock_offset` + // which instead represents the clock offset between a remote sender and the + // capturer. The following holds: + // Capture's NTP Clock = Local NTP Clock + Local-Capture Clock Offset + absl::optional local_capture_clock_offset; }; RtpSource() = delete; - // TODO(bugs.webrtc.org/10739): Remove this constructor once all clients - // migrate to the version with absolute capture time. - RtpSource(int64_t timestamp_ms, - uint32_t source_id, - RtpSourceType source_type, - absl::optional audio_level, - uint32_t rtp_timestamp) - : RtpSource(timestamp_ms, - source_id, - source_type, - rtp_timestamp, - {audio_level, absl::nullopt}) {} - RtpSource(int64_t timestamp_ms, uint32_t source_id, RtpSourceType source_type, @@ -87,6 +85,10 @@ class RtpSource { return extensions_.absolute_capture_time; } + absl::optional local_capture_clock_offset() const { + return extensions_.local_capture_clock_offset; + } + bool operator==(const RtpSource& o) const { return timestamp_ms_ == o.timestamp_ms() && source_id_ == o.source_id() && source_type_ == o.source_type() && diff --git a/api/transport/stun.cc b/api/transport/stun.cc index 87da0058d3..1098c6720e 100644 --- a/api/transport/stun.cc +++ b/api/transport/stun.cc @@ -11,6 +11,7 @@ #include "api/transport/stun.h" #include + #include #include #include @@ -20,8 +21,10 @@ #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/crc32.h" +#include "rtc_base/helpers.h" #include "rtc_base/logging.h" #include "rtc_base/message_digest.h" +#include "system_wrappers/include/metrics.h" using rtc::ByteBufferReader; using rtc::ByteBufferWriter; @@ -34,11 +37,11 @@ const int k127Utf8CharactersLengthInBytes = 508; const int kMessageIntegrityAttributeLength = 20; const int kTheoreticalMaximumAttributeLength = 65535; -uint32_t ReduceTransactionId(const std::string& transaction_id) { +uint32_t ReduceTransactionId(absl::string_view transaction_id) { RTC_DCHECK(transaction_id.length() == cricket::kStunTransactionIdLength || - transaction_id.length() == - cricket::kStunLegacyTransactionIdLength); - ByteBufferReader reader(transaction_id.c_str(), transaction_id.length()); + transaction_id.length() == cricket::kStunLegacyTransactionIdLength) + << transaction_id.length(); + ByteBufferReader reader(transaction_id.data(), transaction_id.size()); uint32_t result = 0; uint32_t next; while (reader.ReadUInt32(&next)) { @@ -102,10 +105,15 @@ const int SERVER_NOT_REACHABLE_ERROR = 701; // StunMessage StunMessage::StunMessage() - : type_(0), - length_(0), - transaction_id_(EMPTY_TRANSACTION_ID), - stun_magic_cookie_(kStunMagicCookie) { + : StunMessage(STUN_INVALID_MESSAGE_TYPE, EMPTY_TRANSACTION_ID) {} + +StunMessage::StunMessage(uint16_t type) + : StunMessage(type, GenerateTransactionId()) {} + +StunMessage::StunMessage(uint16_t type, absl::string_view transaction_id) + : type_(type), + transaction_id_(transaction_id), + reduced_transaction_id_(ReduceTransactionId(transaction_id_)) { RTC_DCHECK(IsValidTransactionId(transaction_id_)); } @@ -118,15 +126,6 @@ bool StunMessage::IsLegacy() const { return false; } -bool StunMessage::SetTransactionID(const std::string& str) { - if (!IsValidTransactionId(str)) { - return false; - } - transaction_id_ = str; - reduced_transaction_id_ = ReduceTransactionId(transaction_id_); - return true; -} - static bool DesignatedExpertRange(int attr_type) { return (attr_type >= 0x4000 && attr_type <= 0x7FFF) || (attr_type >= 0xC000 && attr_type <= 0xFFFF); @@ -240,6 +239,8 @@ const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const { StunMessage::IntegrityStatus StunMessage::ValidateMessageIntegrity( const std::string& password) { + RTC_DCHECK(integrity_ == IntegrityStatus::kNotSet) + << "Usage error: Verification should only be done once"; password_ = password; if (GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) { if (ValidateMessageIntegrityOfType( @@ -260,9 +261,101 @@ StunMessage::IntegrityStatus StunMessage::ValidateMessageIntegrity( } else { integrity_ = IntegrityStatus::kNoIntegrity; } + // Log the result of integrity checking. See crbug.com/1177125 for background. + // Convert args to integer for the benefit of the macros. + int bucket_count = static_cast(IntegrityStatus::kMaxValue) + 1; + int integrity = static_cast(integrity_); + if (IsStunRequestType(type_)) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Request", integrity, + bucket_count); + } else if (IsStunSuccessResponseType(type_)) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Response", integrity, + bucket_count); + } else if (IsStunIndicationType(type_)) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.Stun.Integrity.Indication", integrity, + bucket_count); + } else { + RTC_DCHECK(IsStunErrorResponseType(type_)); + auto* error_attribute = GetErrorCode(); + if (!error_attribute) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.NoErrorAttribute", integrity, + bucket_count); + } else { + switch (error_attribute->code()) { + case STUN_ERROR_TRY_ALTERNATE: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.TryAlternate", integrity, + bucket_count); + break; + case STUN_ERROR_BAD_REQUEST: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.BadRequest", integrity, + bucket_count); + break; + case STUN_ERROR_UNAUTHORIZED: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.Unauthorized", integrity, + bucket_count); + break; + case STUN_ERROR_UNKNOWN_ATTRIBUTE: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.UnknownAttribute", integrity, + bucket_count); + break; + case STUN_ERROR_STALE_NONCE: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.StaleNonce", integrity, + bucket_count); + break; + case STUN_ERROR_SERVER_ERROR: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.ServerError", integrity, + bucket_count); + break; + case STUN_ERROR_GLOBAL_FAILURE: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.GlobalFailure", integrity, + bucket_count); + break; + default: + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.Stun.Integrity.ErrorResponse.ErrorOther", integrity, + bucket_count); + break; + } + } + } return integrity_; } +StunMessage::IntegrityStatus StunMessage::RevalidateMessageIntegrity( + const std::string& password) { + RTC_LOG(LS_INFO) << "Message revalidation, old status was " + << static_cast(integrity_); + integrity_ = IntegrityStatus::kNotSet; + return ValidateMessageIntegrity(password); +} + +bool StunMessage::ValidateMessageIntegrityForTesting( + const char* data, + size_t size, + const std::string& password) { + return ValidateMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY, + kStunMessageIntegritySize, data, size, + password); +} + +bool StunMessage::ValidateMessageIntegrity32ForTesting( + const char* data, + size_t size, + const std::string& password) { + return ValidateMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32, + kStunMessageIntegrity32Size, data, size, + password); +} + +// Deprecated bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size, const std::string& password) { @@ -271,6 +364,7 @@ bool StunMessage::ValidateMessageIntegrity(const char* data, password); } +// Deprecated bool StunMessage::ValidateMessageIntegrity32(const char* data, size_t size, const std::string& password) { @@ -364,22 +458,19 @@ bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type, mi_attr_size) == 0; } -bool StunMessage::AddMessageIntegrity(const std::string& password) { +bool StunMessage::AddMessageIntegrity(absl::string_view password) { return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY, - kStunMessageIntegritySize, password.c_str(), - password.size()); + kStunMessageIntegritySize, password); } bool StunMessage::AddMessageIntegrity32(absl::string_view password) { return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32, - kStunMessageIntegrity32Size, password.data(), - password.length()); + kStunMessageIntegrity32Size, password); } bool StunMessage::AddMessageIntegrityOfType(int attr_type, size_t attr_size, - const char* key, - size_t keylen) { + absl::string_view key) { // Add the attribute with a dummy value. Since this is a known attribute, it // can't fail. RTC_DCHECK(attr_size <= kStunMessageIntegritySize); @@ -396,8 +487,9 @@ bool StunMessage::AddMessageIntegrityOfType(int attr_type, int msg_len_for_hmac = static_cast( buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length()); char hmac[kStunMessageIntegritySize]; - size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1, key, keylen, buf.Data(), - msg_len_for_hmac, hmac, sizeof(hmac)); + size_t ret = + rtc::ComputeHmac(rtc::DIGEST_SHA_1, key.data(), key.size(), buf.Data(), + msg_len_for_hmac, hmac, sizeof(hmac)); RTC_DCHECK(ret == sizeof(hmac)); if (ret != sizeof(hmac)) { RTC_LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity " @@ -407,7 +499,7 @@ bool StunMessage::AddMessageIntegrityOfType(int attr_type, // Insert correct HMAC into the attribute. msg_integrity_attr->CopyBytes(hmac, attr_size); - password_.assign(key, keylen); + password_ = std::string(key); integrity_ = IntegrityStatus::kIntegrityOk; return true; } @@ -442,6 +534,11 @@ bool StunMessage::ValidateFingerprint(const char* data, size_t size) { rtc::ComputeCrc32(data, size - fingerprint_attr_size)); } +// static +std::string StunMessage::GenerateTransactionId() { + return rtc::CreateRandomString(kStunTransactionIdLength); +} + bool StunMessage::IsStunMethod(rtc::ArrayView methods, const char* data, size_t size) { @@ -589,6 +686,12 @@ void StunMessage::SetStunMagicCookie(uint32_t val) { stun_magic_cookie_ = val; } +void StunMessage::SetTransactionIdForTesting(absl::string_view transaction_id) { + RTC_DCHECK(IsValidTransactionId(transaction_id)); + transaction_id_ = std::string(transaction_id); + reduced_transaction_id_ = ReduceTransactionId(transaction_id_); +} + StunAttributeValueType StunMessage::GetAttributeValueType(int type) const { switch (type) { case STUN_ATTR_MAPPED_ADDRESS: @@ -647,7 +750,7 @@ const StunAttribute* StunMessage::GetAttribute(int type) const { return NULL; } -bool StunMessage::IsValidTransactionId(const std::string& transaction_id) { +bool StunMessage::IsValidTransactionId(absl::string_view transaction_id) { return transaction_id.size() == kStunTransactionIdLength || transaction_id.size() == kStunLegacyTransactionIdLength; } @@ -997,9 +1100,9 @@ StunByteStringAttribute::StunByteStringAttribute(uint16_t type) : StunAttribute(type, 0), bytes_(NULL) {} StunByteStringAttribute::StunByteStringAttribute(uint16_t type, - const std::string& str) + absl::string_view str) : StunAttribute(type, 0), bytes_(NULL) { - CopyBytes(str.c_str(), str.size()); + CopyBytes(str); } StunByteStringAttribute::StunByteStringAttribute(uint16_t type, @@ -1020,8 +1123,10 @@ StunAttributeValueType StunByteStringAttribute::value_type() const { return STUN_VALUE_BYTE_STRING; } -void StunByteStringAttribute::CopyBytes(const char* bytes) { - CopyBytes(bytes, strlen(bytes)); +void StunByteStringAttribute::CopyBytes(absl::string_view bytes) { + char* new_bytes = new char[bytes.size()]; + memcpy(new_bytes, bytes.data(), bytes.size()); + SetBytes(new_bytes, bytes.size()); } void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) { diff --git a/api/transport/stun.h b/api/transport/stun.h index 766b9ec368..c2c9ad4b9c 100644 --- a/api/transport/stun.h +++ b/api/transport/stun.h @@ -31,7 +31,8 @@ namespace cricket { // These are the types of STUN messages defined in RFC 5389. -enum StunMessageType { +enum StunMessageType : uint16_t { + STUN_INVALID_MESSAGE_TYPE = 0x0000, STUN_BINDING_REQUEST = 0x0001, STUN_BINDING_INDICATION = 0x0011, STUN_BINDING_RESPONSE = 0x0101, @@ -144,16 +145,28 @@ class StunXorAddressAttribute; // that attribute class. class StunMessage { public: + // Constructs a StunMessage with an invalid type and empty, legacy length + // (16 bytes, RFC3489) transaction id. StunMessage(); + + // Construct a `StunMessage` with a specific type and generate a new + // 12 byte transaction id (RFC5389). + explicit StunMessage(uint16_t type); + + StunMessage(uint16_t type, absl::string_view transaction_id); + virtual ~StunMessage(); // The verification status of the message. This is checked on parsing, // or set by AddMessageIntegrity. + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. enum class IntegrityStatus { - kNotSet, - kNoIntegrity, // Message-integrity attribute missing - kIntegrityOk, // Message-integrity checked OK - kIntegrityBad, // Message-integrity verification failed + kNotSet = 0, + kNoIntegrity = 1, // Message-integrity attribute missing + kIntegrityOk = 2, // Message-integrity checked OK + kIntegrityBad = 3, // Message-integrity verification failed + kMaxValue = kIntegrityBad, }; int type() const { return type_; } @@ -168,8 +181,13 @@ class StunMessage { // is determined by the lengths of the transaction ID. bool IsLegacy() const; - void SetType(int type) { type_ = static_cast(type); } - bool SetTransactionID(const std::string& str); + [[deprecated]] void SetType(int type) { type_ = static_cast(type); } + [[deprecated]] bool SetTransactionID(absl::string_view transaction_id) { + if (!IsValidTransactionId(transaction_id)) + return false; + SetTransactionIdForTesting(transaction_id); + return true; + } // Get a list of all of the attribute types in the "comprehension required" // range that were not recognized. @@ -202,6 +220,11 @@ class StunMessage { // This uses the buffered raw-format message stored by Read(). IntegrityStatus ValidateMessageIntegrity(const std::string& password); + // Revalidates the STUN message with (possibly) a new password. + // Indicates that calling logic needs review - probably previous call + // was checking with the wrong password. + IntegrityStatus RevalidateMessageIntegrity(const std::string& password); + // Returns the current integrity status of the message. IntegrityStatus integrity() const { return integrity_; } @@ -218,7 +241,7 @@ class StunMessage { } // Adds a MESSAGE-INTEGRITY attribute that is valid for the current message. - bool AddMessageIntegrity(const std::string& password); + bool AddMessageIntegrity(absl::string_view password); // Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the // current message. @@ -233,6 +256,9 @@ class StunMessage { // Verifies that a given buffer is STUN by checking for a correct FINGERPRINT. static bool ValidateFingerprint(const char* data, size_t size); + // Generates a new 12 byte (RFC5389) transaction id. + static std::string GenerateTransactionId(); + // Adds a FINGERPRINT attribute that is valid for the current message. bool AddFingerprint(); @@ -249,7 +275,10 @@ class StunMessage { // Modify the stun magic cookie used for this STUN message. // This is used for testing. - void SetStunMagicCookie(uint32_t val); + [[deprecated]] void SetStunMagicCookie(uint32_t val); + + // Change the internal transaction id. Used only for testing. + void SetTransactionIdForTesting(absl::string_view transaction_id); // Contruct a copy of `this`. std::unique_ptr Clone() const; @@ -259,29 +288,27 @@ class StunMessage { bool EqualAttributes(const StunMessage* other, std::function attribute_type_mask) const; - // Expose raw-buffer ValidateMessageIntegrity function for testing. - static bool ValidateMessageIntegrityForTesting(const char* data, - size_t size, - const std::string& password) { - return ValidateMessageIntegrity(data, size, password); - } - // Expose raw-buffer ValidateMessageIntegrity function for testing. - static bool ValidateMessageIntegrity32ForTesting( - const char* data, - size_t size, - const std::string& password) { - return ValidateMessageIntegrity32(data, size, password); - } // Validates that a STUN message in byte buffer form // has a correct MESSAGE-INTEGRITY value. // These functions are not recommended and will be deprecated; use // ValidateMessageIntegrity(password) on the parsed form instead. - static bool ValidateMessageIntegrity(const char* data, - size_t size, - const std::string& password); - static bool ValidateMessageIntegrity32(const char* data, - size_t size, - const std::string& password); + [[deprecated("Use member function")]] static bool ValidateMessageIntegrity( + const char* data, + size_t size, + const std::string& password); + [[deprecated("Use member function")]] static bool ValidateMessageIntegrity32( + const char* data, + size_t size, + const std::string& password); + + // Expose raw-buffer ValidateMessageIntegrity function for testing. + static bool ValidateMessageIntegrityForTesting(const char* data, + size_t size, + const std::string& password); + // Expose raw-buffer ValidateMessageIntegrity function for testing. + static bool ValidateMessageIntegrity32ForTesting(const char* data, + size_t size, + const std::string& password); protected: // Verifies that the given attribute is allowed for this message. @@ -292,22 +319,21 @@ class StunMessage { private: StunAttribute* CreateAttribute(int type, size_t length) /* const*/; const StunAttribute* GetAttribute(int type) const; - static bool IsValidTransactionId(const std::string& transaction_id); + static bool IsValidTransactionId(absl::string_view transaction_id); bool AddMessageIntegrityOfType(int mi_attr_type, size_t mi_attr_size, - const char* key, - size_t keylen); + absl::string_view key); static bool ValidateMessageIntegrityOfType(int mi_attr_type, size_t mi_attr_size, const char* data, size_t size, const std::string& password); - uint16_t type_; - uint16_t length_; + uint16_t type_ = STUN_INVALID_MESSAGE_TYPE; + uint16_t length_ = 0; std::string transaction_id_; - uint32_t reduced_transaction_id_; - uint32_t stun_magic_cookie_; + uint32_t reduced_transaction_id_ = 0; + uint32_t stun_magic_cookie_ = kStunMagicCookie; // The original buffer for messages created by Read(). std::string buffer_; IntegrityStatus integrity_ = IntegrityStatus::kNotSet; @@ -486,7 +512,7 @@ class StunUInt64Attribute : public StunAttribute { class StunByteStringAttribute : public StunAttribute { public: explicit StunByteStringAttribute(uint16_t type); - StunByteStringAttribute(uint16_t type, const std::string& str); + StunByteStringAttribute(uint16_t type, absl::string_view str); StunByteStringAttribute(uint16_t type, const void* bytes, size_t length); StunByteStringAttribute(uint16_t type, uint16_t length); ~StunByteStringAttribute() override; @@ -494,10 +520,16 @@ class StunByteStringAttribute : public StunAttribute { StunAttributeValueType value_type() const override; const char* bytes() const { return bytes_; } - std::string GetString() const { return std::string(bytes_, length()); } + absl::string_view string_view() const { + return absl::string_view(bytes_, length()); + } + + [[deprecated]] std::string GetString() const { + return std::string(bytes_, length()); + } - void CopyBytes(const char* bytes); // uses strlen void CopyBytes(const void* bytes, size_t length); + void CopyBytes(absl::string_view bytes); uint8_t GetByte(size_t index) const; void SetByte(size_t index, uint8_t value); @@ -635,13 +667,16 @@ enum RelayAttributeType { // A "GTURN" STUN message. class RelayMessage : public StunMessage { + public: + using StunMessage::StunMessage; + protected: StunAttributeValueType GetAttributeValueType(int type) const override; StunMessage* CreateNew() const override; }; // Defined in TURN RFC 5766. -enum TurnMessageType { +enum TurnMessageType : uint16_t { STUN_ALLOCATE_REQUEST = 0x0003, STUN_ALLOCATE_RESPONSE = 0x0103, STUN_ALLOCATE_ERROR_RESPONSE = 0x0113, @@ -689,6 +724,9 @@ extern const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[]; extern const char STUN_ERROR_REASON_WRONG_CREDENTIALS[]; extern const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[]; class TurnMessage : public StunMessage { + public: + using StunMessage::StunMessage; + protected: StunAttributeValueType GetAttributeValueType(int type) const override; StunMessage* CreateNew() const override; @@ -747,6 +785,9 @@ extern const char STUN_ERROR_REASON_ROLE_CONFLICT[]; // A RFC 5245 ICE STUN message. class IceMessage : public StunMessage { + public: + using StunMessage::StunMessage; + protected: StunAttributeValueType GetAttributeValueType(int type) const override; StunMessage* CreateNew() const override; diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc index e180703817..96ad45843b 100644 --- a/api/transport/stun_unittest.cc +++ b/api/transport/stun_unittest.cc @@ -20,6 +20,7 @@ #include "rtc_base/byte_buffer.h" #include "rtc_base/byte_order.h" #include "rtc_base/socket_address.h" +#include "system_wrappers/include/metrics.h" #include "test/gtest.h" namespace cricket { @@ -650,12 +651,12 @@ TEST_F(StunTest, ReadRfc5769RequestMessage) { const StunByteStringAttribute* software = msg.GetByteString(STUN_ATTR_SOFTWARE); ASSERT_TRUE(software != NULL); - EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->GetString()); + EXPECT_EQ(kRfc5769SampleMsgClientSoftware, software->string_view()); const StunByteStringAttribute* username = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(username != NULL); - EXPECT_EQ(kRfc5769SampleMsgUsername, username->GetString()); + EXPECT_EQ(kRfc5769SampleMsgUsername, username->string_view()); // Actual M-I value checked in a later test. ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL); @@ -677,7 +678,7 @@ TEST_F(StunTest, ReadRfc5769ResponseMessage) { const StunByteStringAttribute* software = msg.GetByteString(STUN_ATTR_SOFTWARE); ASSERT_TRUE(software != NULL); - EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString()); + EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view()); const StunAddressAttribute* mapped_address = msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS); @@ -700,7 +701,7 @@ TEST_F(StunTest, ReadRfc5769ResponseMessageIPv6) { const StunByteStringAttribute* software = msg.GetByteString(STUN_ATTR_SOFTWARE); ASSERT_TRUE(software != NULL); - EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->GetString()); + EXPECT_EQ(kRfc5769SampleMsgServerSoftware, software->string_view()); const StunAddressAttribute* mapped_address = msg.GetAddress(STUN_ATTR_XOR_MAPPED_ADDRESS); @@ -723,15 +724,15 @@ TEST_F(StunTest, ReadRfc5769RequestMessageLongTermAuth) { const StunByteStringAttribute* username = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(username != NULL); - EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->GetString()); + EXPECT_EQ(kRfc5769SampleMsgWithAuthUsername, username->string_view()); const StunByteStringAttribute* nonce = msg.GetByteString(STUN_ATTR_NONCE); ASSERT_TRUE(nonce != NULL); - EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->GetString()); + EXPECT_EQ(kRfc5769SampleMsgWithAuthNonce, nonce->string_view()); const StunByteStringAttribute* realm = msg.GetByteString(STUN_ATTR_REALM); ASSERT_TRUE(realm != NULL); - EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->GetString()); + EXPECT_EQ(kRfc5769SampleMsgWithAuthRealm, realm->string_view()); // No fingerprint, actual M-I checked in later tests. ASSERT_TRUE(msg.GetByteString(STUN_ATTR_MESSAGE_INTEGRITY) != NULL); @@ -761,7 +762,6 @@ TEST_F(StunTest, ReadLegacyMessage) { TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) { StunMessage msg; - StunMessage msg2; size_t size = ReadStunMessage(&msg, kStunMessageWithIPv6XorMappedAddress); rtc::IPAddress test_address(kIPv6TestAddress1); @@ -775,7 +775,7 @@ TEST_F(StunTest, SetIPv6XorAddressAttributeOwner) { test_address); // Owner with a different transaction ID. - msg2.SetTransactionID("ABCDABCDABCD"); + StunMessage msg2(STUN_INVALID_MESSAGE_TYPE, "ABCDABCDABCD"); StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20, NULL); addr2.SetIP(addr->ipaddr()); addr2.SetPort(addr->port()); @@ -809,7 +809,6 @@ TEST_F(StunTest, SetIPv4XorAddressAttributeOwner) { // should _not_ be affected by a change in owner. IPv4 XOR address uses the // magic cookie value which is fixed. StunMessage msg; - StunMessage msg2; size_t size = ReadStunMessage(&msg, kStunMessageWithIPv4XorMappedAddress); rtc::IPAddress test_address(kIPv4TestAddress1); @@ -823,7 +822,7 @@ TEST_F(StunTest, SetIPv4XorAddressAttributeOwner) { test_address); // Owner with a different transaction ID. - msg2.SetTransactionID("ABCDABCDABCD"); + StunMessage msg2(STUN_INVALID_MESSAGE_TYPE, "ABCDABCDABCD"); StunXorAddressAttribute addr2(STUN_ATTR_XOR_MAPPED_ADDRESS, 20, NULL); addr2.SetIP(addr->ipaddr()); addr2.SetPort(addr->port()); @@ -893,13 +892,12 @@ TEST_F(StunTest, CreateAddressInArbitraryOrder) { } TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithIPv6MappedAddress); rtc::IPAddress test_ip(kIPv6TestAddress1); - msg.SetType(STUN_BINDING_REQUEST); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_REQUEST, std::string(reinterpret_cast(kTestTransactionId1), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength); @@ -922,13 +920,12 @@ TEST_F(StunTest, WriteMessageWithIPv6AddressAttribute) { } TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithIPv4MappedAddress); rtc::IPAddress test_ip(kIPv4TestAddress1); - msg.SetType(STUN_BINDING_RESPONSE); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_RESPONSE, std::string(reinterpret_cast(kTestTransactionId1), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength); @@ -951,13 +948,12 @@ TEST_F(StunTest, WriteMessageWithIPv4AddressAttribute) { } TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithIPv6XorMappedAddress); rtc::IPAddress test_ip(kIPv6TestAddress1); - msg.SetType(STUN_BINDING_RESPONSE); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_RESPONSE, std::string(reinterpret_cast(kTestTransactionId2), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength); @@ -981,13 +977,12 @@ TEST_F(StunTest, WriteMessageWithIPv6XorAddressAttribute) { } TEST_F(StunTest, WriteMessageWithIPv4XoreAddressAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithIPv4XorMappedAddress); rtc::IPAddress test_ip(kIPv4TestAddress1); - msg.SetType(STUN_BINDING_RESPONSE); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_RESPONSE, std::string(reinterpret_cast(kTestTransactionId1), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength); @@ -1019,7 +1014,7 @@ TEST_F(StunTest, ReadByteStringAttribute) { const StunByteStringAttribute* username = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(username != NULL); - EXPECT_EQ(kTestUserName1, username->GetString()); + EXPECT_EQ(kTestUserName1, username->string_view()); } TEST_F(StunTest, ReadPaddedByteStringAttribute) { @@ -1032,7 +1027,7 @@ TEST_F(StunTest, ReadPaddedByteStringAttribute) { const StunByteStringAttribute* username = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(username != NULL); - EXPECT_EQ(kTestUserName2, username->GetString()); + EXPECT_EQ(kTestUserName2, username->string_view()); } TEST_F(StunTest, ReadErrorCodeAttribute) { @@ -1079,15 +1074,14 @@ TEST_F(StunTest, ReadMessageWithAnUnknownAttribute) { const StunByteStringAttribute* username = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(username != NULL); - EXPECT_EQ(kTestUserName2, username->GetString()); + EXPECT_EQ(kTestUserName2, username->string_view()); } TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithErrorAttribute); - msg.SetType(STUN_BINDING_ERROR_RESPONSE); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_ERROR_RESPONSE, std::string(reinterpret_cast(kTestTransactionId1), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId1, kStunTransactionIdLength); @@ -1105,11 +1099,10 @@ TEST_F(StunTest, WriteMessageWithAnErrorCodeAttribute) { } TEST_F(StunTest, WriteMessageWithAUInt16ListAttribute) { - StunMessage msg; size_t size = sizeof(kStunMessageWithUInt16ListAttribute); - msg.SetType(STUN_BINDING_REQUEST); - msg.SetTransactionID( + StunMessage msg( + STUN_BINDING_REQUEST, std::string(reinterpret_cast(kTestTransactionId2), kStunTransactionIdLength)); CheckStunTransactionID(msg, kTestTransactionId2, kStunTransactionIdLength); @@ -1475,7 +1468,7 @@ static const unsigned char kRelayMessage[] = { // Test that we can read the GTURN-specific fields. TEST_F(StunTest, ReadRelayMessage) { - RelayMessage msg, msg2; + RelayMessage msg; const char* input = reinterpret_cast(kRelayMessage); size_t size = sizeof(kRelayMessage); @@ -1486,8 +1479,7 @@ TEST_F(StunTest, ReadRelayMessage) { EXPECT_EQ(size - 20, msg.length()); EXPECT_EQ("0123456789ab", msg.transaction_id()); - msg2.SetType(STUN_BINDING_REQUEST); - msg2.SetTransactionID("0123456789ab"); + RelayMessage msg2(STUN_BINDING_REQUEST, "0123456789ab"); in_addr legacy_in_addr; legacy_in_addr.s_addr = htonl(17U); @@ -1507,7 +1499,7 @@ TEST_F(StunTest, ReadRelayMessage) { const StunByteStringAttribute* bytes = msg.GetByteString(STUN_ATTR_USERNAME); ASSERT_TRUE(bytes != NULL); EXPECT_EQ(12U, bytes->length()); - EXPECT_EQ("abcdefghijkl", bytes->GetString()); + EXPECT_EQ("abcdefghijkl", bytes->string_view()); auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME); bytes2->CopyBytes("abcdefghijkl"); @@ -1565,7 +1557,7 @@ TEST_F(StunTest, ReadRelayMessage) { bytes = msg.GetByteString(STUN_ATTR_DATA); ASSERT_TRUE(bytes != NULL); EXPECT_EQ(7U, bytes->length()); - EXPECT_EQ("abcdefg", bytes->GetString()); + EXPECT_EQ("abcdefg", bytes->string_view()); bytes2 = StunAttribute::CreateByteString(STUN_ATTR_DATA); bytes2->CopyBytes("abcdefg"); @@ -1710,7 +1702,7 @@ TEST_F(StunTest, CopyAttribute) { // Test Clone TEST_F(StunTest, Clone) { - IceMessage msg; + IceMessage msg(0, "0123456789ab"); { auto errorcode = StunAttribute::CreateErrorCode(); errorcode->SetCode(kTestErrorCode); @@ -1736,9 +1728,6 @@ TEST_F(StunTest, Clone) { auto copy = msg.Clone(); ASSERT_NE(nullptr, copy.get()); - msg.SetTransactionID("0123456789ab"); - copy->SetTransactionID("0123456789ab"); - rtc::ByteBufferWriter out1; EXPECT_TRUE(msg.Write(&out1)); rtc::ByteBufferWriter out2; @@ -1812,21 +1801,18 @@ TEST_F(StunTest, EqualAttributes) { } TEST_F(StunTest, ReduceTransactionIdIsHostOrderIndependent) { - std::string transaction_id = "abcdefghijkl"; - StunMessage message; - ASSERT_TRUE(message.SetTransactionID(transaction_id)); + const std::string transaction_id = "abcdefghijkl"; + StunMessage message(0, transaction_id); uint32_t reduced_transaction_id = message.reduced_transaction_id(); EXPECT_EQ(reduced_transaction_id, 1835954016u); } TEST_F(StunTest, GoogMiscInfo) { - StunMessage msg; + StunMessage msg(STUN_BINDING_REQUEST, "ABCDEFGHIJKL"); const size_t size = /* msg header */ 20 + /* attr header */ 4 + /* 3 * 2 rounded to multiple of 4 */ 8; - msg.SetType(STUN_BINDING_REQUEST); - msg.SetTransactionID("ABCDEFGH"); auto list = StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO); list->AddTypeAtIndex(0, 0x1U); @@ -1861,9 +1847,7 @@ TEST_F(StunTest, IsStunMethod) { } TEST_F(StunTest, SizeRestrictionOnAttributes) { - StunMessage msg; - msg.SetType(STUN_BINDING_REQUEST); - msg.SetTransactionID("ABCDEFGH"); + StunMessage msg(STUN_BINDING_REQUEST, "ABCDEFGHIJKL"); auto long_username = StunAttribute::CreateByteString(STUN_ATTR_USERNAME); std::string long_string(509, 'x'); long_username->CopyBytes(long_string.c_str(), long_string.size()); @@ -1872,4 +1856,27 @@ TEST_F(StunTest, SizeRestrictionOnAttributes) { ASSERT_FALSE(msg.Write(&out)); } +TEST_F(StunTest, ValidateMessageIntegrityWithParser) { + webrtc::metrics::Reset(); // Ensure counters start from zero. + // Try the messages from RFC 5769. + StunMessage message; + rtc::ByteBufferReader reader( + reinterpret_cast(kRfc5769SampleRequest), + sizeof(kRfc5769SampleRequest)); + EXPECT_TRUE(message.Read(&reader)); + EXPECT_EQ(message.ValidateMessageIntegrity(kRfc5769SampleMsgPassword), + StunMessage::IntegrityStatus::kIntegrityOk); + EXPECT_EQ(webrtc::metrics::NumEvents( + "WebRTC.Stun.Integrity.Request", + static_cast(StunMessage::IntegrityStatus::kIntegrityOk)), + 1); + EXPECT_EQ(message.RevalidateMessageIntegrity("Invalid password"), + StunMessage::IntegrityStatus::kIntegrityBad); + EXPECT_EQ(webrtc::metrics::NumEvents( + "WebRTC.Stun.Integrity.Request", + static_cast(StunMessage::IntegrityStatus::kIntegrityBad)), + 1); + EXPECT_EQ(webrtc::metrics::NumSamples("WebRTC.Stun.Integrity.Request"), 2); +} + } // namespace cricket diff --git a/api/turn_customizer.h b/api/turn_customizer.h index 50e406516e..8d569b36d2 100644 --- a/api/turn_customizer.h +++ b/api/turn_customizer.h @@ -13,9 +13,10 @@ #include +#include "api/transport/stun.h" + namespace cricket { class PortInterface; -class StunMessage; } // namespace cricket namespace webrtc { diff --git a/api/uma_metrics.h b/api/uma_metrics.h index a975b82aeb..a63159e849 100644 --- a/api/uma_metrics.h +++ b/api/uma_metrics.h @@ -83,14 +83,6 @@ enum IceCandidatePairType { // occurrences of events, while "Name" has a value associated with it which is // used to form a histogram. -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum KeyExchangeProtocolType { - kEnumCounterKeyProtocolDtls = 0, - kEnumCounterKeyProtocolSdes = 1, - kEnumCounterKeyProtocolMax -}; - // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. enum KeyExchangeProtocolMedia { @@ -112,36 +104,6 @@ enum SdpSemanticRequested { kSdpSemanticRequestMax }; -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum SdpSemanticNegotiated { - kSdpSemanticNegotiatedNone = 0, - kSdpSemanticNegotiatedPlanB = 1, - kSdpSemanticNegotiatedUnifiedPlan = 2, - kSdpSemanticNegotiatedMixed = 3, - kSdpSemanticNegotiatedMax -}; - -// Metric which records the format of the received SDP for tracking how much the -// difference between Plan B and Unified Plan affect users. -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum SdpFormatReceived { - // No audio or video tracks. This is worth special casing since it seems to be - // the most common scenario (data-channel only). - kSdpFormatReceivedNoTracks = 0, - // No more than one audio and one video track. Should be compatible with both - // Plan B and Unified Plan endpoints. - kSdpFormatReceivedSimple = 1, - // More than one audio track or more than one video track in the Plan B format - // (e.g., one audio media section with multiple streams). - kSdpFormatReceivedComplexPlanB = 2, - // More than one audio track or more than one video track in the Unified Plan - // format (e.g., two audio media sections). - kSdpFormatReceivedComplexUnifiedPlan = 3, - kSdpFormatReceivedMax -}; - // Metric for counting the outcome of adding an ICE candidate // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. @@ -213,6 +175,16 @@ enum ProvisionalAnswerUsage { kProvisionalAnswerMax }; +// Metrics for RTCRtpMuxPolicy. The only defined value is +// https://w3c.github.io/webrtc-pc/#rtcrtcpmuxpolicy-enum +// "require" but there is a legacy option "negotiate" which +// was removed from the spec. +enum RtcpMuxPolicyUsage { + kRtcpMuxPolicyUsageRequire = 0, + kRtcpMuxPolicyUsageNegotiate = 1, + kRtcpMuxPolicyUsageMax +}; + // When adding new metrics please consider using the style described in // https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#usage // instead of the legacy enums used above. diff --git a/api/units/data_rate.h b/api/units/data_rate.h index 98572123c5..d813c61156 100644 --- a/api/units/data_rate.h +++ b/api/units/data_rate.h @@ -23,7 +23,7 @@ #include "api/units/frequency.h" #include "api/units/time_delta.h" #include "rtc_base/checks.h" -#include "rtc_base/units/unit_base.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export namespace webrtc { // DataRate is a class that represents a given data rate. This can be used to diff --git a/api/units/data_size.h b/api/units/data_size.h index 6817e24c26..9df6434fb9 100644 --- a/api/units/data_size.h +++ b/api/units/data_size.h @@ -18,7 +18,7 @@ #include #include -#include "rtc_base/units/unit_base.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export namespace webrtc { // DataSize is a class represeting a count of bytes. diff --git a/api/units/frequency.h b/api/units/frequency.h index 8e9cc2b5f4..06081e4c0d 100644 --- a/api/units/frequency.h +++ b/api/units/frequency.h @@ -20,7 +20,7 @@ #include #include "api/units/time_delta.h" -#include "rtc_base/units/unit_base.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export namespace webrtc { diff --git a/api/units/time_delta.h b/api/units/time_delta.h index d5951005e3..5981e32dce 100644 --- a/api/units/time_delta.h +++ b/api/units/time_delta.h @@ -19,7 +19,7 @@ #include #include -#include "rtc_base/units/unit_base.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export namespace webrtc { diff --git a/api/units/timestamp.h b/api/units/timestamp.h index 1e9f9d1dc5..8aabe05cad 100644 --- a/api/units/timestamp.h +++ b/api/units/timestamp.h @@ -20,6 +20,7 @@ #include "api/units/time_delta.h" #include "rtc_base/checks.h" +#include "rtc_base/units/unit_base.h" // IWYU pragma: export namespace webrtc { // Timestamp represents the time that has passed since some unspecified epoch. diff --git a/api/video/BUILD.gn b/api/video/BUILD.gn index 6057a7386d..17277562e1 100644 --- a/api/video/BUILD.gn +++ b/api/video/BUILD.gn @@ -65,6 +65,7 @@ rtc_library("video_frame") { deps = [ ":video_rtp_headers", "..:array_view", + "..:make_ref_counted", "..:rtp_packet_info", "..:scoped_refptr", "..:video_track_source_constraints", @@ -89,12 +90,16 @@ rtc_library("video_frame_i010") { sources = [ "i010_buffer.cc", "i010_buffer.h", + "i210_buffer.cc", + "i210_buffer.h", + "i410_buffer.cc", + "i410_buffer.h", ] deps = [ ":video_frame", ":video_rtp_headers", + "..:make_ref_counted", "..:scoped_refptr", - "../../rtc_base", "../../rtc_base:checks", "../../rtc_base:refcount", "../../rtc_base/memory:aligned_malloc", @@ -111,8 +116,8 @@ rtc_source_set("recordable_encoded_frame") { ":video_frame", ":video_rtp_headers", "..:array_view", + "..:make_ref_counted", "..:scoped_refptr", - "../../rtc_base:refcount", "../units:timestamp", ] } @@ -127,6 +132,11 @@ rtc_source_set("render_resolution") { public = [ "render_resolution.h" ] } +rtc_source_set("resolution") { + visibility = [ "*" ] + public = [ "resolution.h" ] +} + rtc_library("encoded_image") { visibility = [ "*" ] sources = [ @@ -176,6 +186,7 @@ rtc_library("rtp_video_frame_assembler") { "../../modules/video_coding:packet_buffer", "../../modules/video_coding:video_coding", "../../rtc_base:logging", + "../../rtc_base:rtc_numerics", ] absl_deps = [ @@ -248,37 +259,6 @@ rtc_source_set("video_bitrate_allocator_factory") { ] } -rtc_source_set("video_stream_decoder") { - visibility = [ "*" ] - sources = [ "video_stream_decoder.h" ] - - deps = [ - ":encoded_frame", - ":video_frame", - ":video_rtp_headers", - "../task_queue", - "../units:time_delta", - "../video_codecs:video_codecs_api", - ] - absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ] -} - -rtc_library("video_stream_decoder_create") { - visibility = [ "*" ] - sources = [ - "video_stream_decoder_create.cc", - "video_stream_decoder_create.h", - ] - - deps = [ - ":video_stream_decoder", - "../../api:field_trials_view", - "../../video:video_stream_decoder_impl", - "../task_queue", - "../video_codecs:video_codecs_api", - ] -} - rtc_library("video_adaptation") { visibility = [ "*" ] sources = [ @@ -295,11 +275,7 @@ rtc_library("video_adaptation") { rtc_source_set("video_stream_encoder") { visibility = [ "*" ] - sources = [ - "video_stream_encoder_interface.h", - "video_stream_encoder_observer.h", - "video_stream_encoder_settings.h", - ] + sources = [ "video_stream_encoder_settings.h" ] deps = [ ":video_adaptation", @@ -327,13 +303,18 @@ rtc_source_set("video_frame_metadata") { "video_frame_metadata.h", ] deps = [ + ":video_frame", + ":video_frame_type", + ":video_rtp_headers", "..:array_view", - "../../modules/rtp_rtcp:rtp_video_header", + "../../modules/video_coding:codec_globals_headers", + "../../rtc_base/system:rtc_export", "../transport/rtp:dependency_descriptor", ] absl_deps = [ "//third_party/abseil-cpp/absl/container:inlined_vector", "//third_party/abseil-cpp/absl/types:optional", + "//third_party/abseil-cpp/absl/types:variant", ] } @@ -358,6 +339,7 @@ rtc_library("builtin_video_bitrate_allocator_factory") { } rtc_library("frame_buffer") { + visibility = [ "*" ] sources = [ "frame_buffer.cc", "frame_buffer.h", @@ -390,21 +372,3 @@ rtc_library("frame_buffer_unittest") { "../../test:test_support", ] } - -if (rtc_include_tests) { - rtc_library("video_unittests") { - testonly = true - sources = [ - "video_frame_metadata_unittest.cc", - "video_stream_decoder_create_unittest.cc", - ] - deps = [ - ":video_frame_metadata", - ":video_stream_decoder_create", - "../../modules/rtp_rtcp:rtp_video_header", - "../../test:test_support", - "../task_queue:default_task_queue_factory", - "../video_codecs:builtin_video_decoder_factory", - ] - } -} diff --git a/api/video/DEPS b/api/video/DEPS index 967b8e7f9d..c84299f943 100644 --- a/api/video/DEPS +++ b/api/video/DEPS @@ -14,6 +14,14 @@ specific_include_rules = { "+rtc_base/memory/aligned_malloc.h", ], + "i210_buffer\.h": [ + "+rtc_base/memory/aligned_malloc.h", + ], + + "i410_buffer\.h": [ + "+rtc_base/memory/aligned_malloc.h", + ], + "i420_buffer\.h": [ "+rtc_base/memory/aligned_malloc.h", ], @@ -41,6 +49,12 @@ specific_include_rules = { "+rtc_base/ref_count.h", ], + "video_frame_metadata\.h": [ + "+modules/video_coding/codecs/h264/include/h264_globals.h", + "+modules/video_coding/codecs/vp8/include/vp8_globals.h", + "+modules/video_coding/codecs/vp9/include/vp9_globals.h", + ], + "video_stream_decoder_create.cc": [ "+video/video_stream_decoder_impl.h", ], diff --git a/api/video/OWNERS b/api/video/OWNERS index e4a16c360a..49b62f3780 100644 --- a/api/video/OWNERS +++ b/api/video/OWNERS @@ -1,5 +1,5 @@ brandtr@webrtc.org magjed@webrtc.org -nisse@webrtc.org +philipel@webrtc.org per-file video_timing.h=ilnik@webrtc.org diff --git a/api/video/encoded_image.cc b/api/video/encoded_image.cc index fc77b9415b..ff61994dee 100644 --- a/api/video/encoded_image.cc +++ b/api/video/encoded_image.cc @@ -13,8 +13,6 @@ #include #include -#include "rtc_base/ref_counted_object.h" - namespace webrtc { EncodedImageBuffer::EncodedImageBuffer(size_t size) : size_(size) { diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h index 88df34916b..7b2f5c88f0 100644 --- a/api/video/encoded_image.h +++ b/api/video/encoded_image.h @@ -78,9 +78,8 @@ class RTC_EXPORT EncodedImage { EncodedImage& operator=(EncodedImage&&); EncodedImage& operator=(const EncodedImage&); - // TODO(nisse): Change style to timestamp(), set_timestamp(), for consistency - // with the VideoFrame class. - // Set frame timestamp (90kHz). + // TODO(bugs.webrtc.org/9378): Change style to timestamp(), set_timestamp(), + // for consistency with the VideoFrame class. Set frame timestamp (90kHz). void SetTimestamp(uint32_t timestamp) { timestamp_rtp_ = timestamp; } // Get frame timestamp (90kHz). @@ -90,6 +89,26 @@ class RTC_EXPORT EncodedImage { int64_t NtpTimeMs() const { return ntp_time_ms_; } + // Every simulcast layer (= encoding) has its own encoder and RTP stream. + // There can be no dependencies between different simulcast layers. + absl::optional SimulcastIndex() const { return simulcast_index_; } + void SetSimulcastIndex(absl::optional simulcast_index) { + RTC_DCHECK_GE(simulcast_index.value_or(0), 0); + RTC_DCHECK_LT(simulcast_index.value_or(0), kMaxSimulcastStreams); + simulcast_index_ = simulcast_index; + } + + const absl::optional& CaptureTimeIdentifier() const { + return capture_time_identifier_; + } + void SetCaptureTimeIdentifier( + const absl::optional& capture_time_identifier) { + capture_time_identifier_ = capture_time_identifier; + } + + // Encoded images can have dependencies between spatial and/or temporal + // layers, depending on the scalability mode used by the encoder. See diagrams + // at https://w3c.github.io/webrtc-svc/#dependencydiagrams*. absl::optional SpatialIndex() const { return spatial_index_; } void SetSpatialIndex(absl::optional spatial_index) { RTC_DCHECK_GE(spatial_index.value_or(0), 0); @@ -97,6 +116,13 @@ class RTC_EXPORT EncodedImage { spatial_index_ = spatial_index; } + absl::optional TemporalIndex() const { return temporal_index_; } + void SetTemporalIndex(absl::optional temporal_index) { + RTC_DCHECK_GE(temporal_index_.value_or(0), 0); + RTC_DCHECK_LT(temporal_index_.value_or(0), kMaxTemporalStreams); + temporal_index_ = temporal_index; + } + // These methods can be used to set/get size of subframe with spatial index // `spatial_index` on encoded frames that consist of multiple spatial layers. absl::optional SpatialLayerFrameSize(int spatial_index) const; @@ -198,7 +224,10 @@ class RTC_EXPORT EncodedImage { rtc::scoped_refptr encoded_data_; size_t size_ = 0; // Size of encoded frame data. uint32_t timestamp_rtp_ = 0; + absl::optional simulcast_index_; + absl::optional capture_time_identifier_; absl::optional spatial_index_; + absl::optional temporal_index_; std::map spatial_layer_frame_size_bytes_; absl::optional color_space_; // This field is meant for media quality testing purpose only. When enabled it diff --git a/api/video/frame_buffer.cc b/api/video/frame_buffer.cc index 67d5f6787f..4cdf2212a6 100644 --- a/api/video/frame_buffer.cc +++ b/api/video/frame_buffer.cc @@ -67,11 +67,11 @@ FrameBuffer::FrameBuffer(int max_size, max_size_(max_size), decoded_frame_history_(max_decode_history) {} -void FrameBuffer::InsertFrame(std::unique_ptr frame) { +bool FrameBuffer::InsertFrame(std::unique_ptr frame) { if (!ValidReferences(*frame)) { RTC_DLOG(LS_WARNING) << "Frame " << frame->Id() << " has invalid references, dropping frame."; - return; + return false; } if (frame->Id() <= decoded_frame_history_.GetLastDecodedFrameId()) { @@ -84,7 +84,7 @@ void FrameBuffer::InsertFrame(std::unique_ptr frame) { Clear(); } else { // Already decoded past this frame. - return; + return false; } } @@ -95,7 +95,7 @@ void FrameBuffer::InsertFrame(std::unique_ptr frame) { Clear(); } else { // No space for this frame. - return; + return false; } } @@ -103,7 +103,7 @@ void FrameBuffer::InsertFrame(std::unique_ptr frame) { auto insert_res = frames_.emplace(frame_id, FrameInfo{std::move(frame)}); if (!insert_res.second) { // Frame has already been inserted. - return; + return false; } if (frames_.size() == max_size_) { @@ -113,6 +113,7 @@ void FrameBuffer::InsertFrame(std::unique_ptr frame) { PropagateContinuity(insert_res.first); FindNextAndLastDecodableTemporalUnit(); + return true; } absl::InlinedVector, 4> diff --git a/api/video/frame_buffer.h b/api/video/frame_buffer.h index 5ab88c50ee..94edf64d5a 100644 --- a/api/video/frame_buffer.h +++ b/api/video/frame_buffer.h @@ -18,7 +18,6 @@ #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" @@ -36,7 +35,7 @@ class FrameBuffer { uint32_t last_rtp_timestamp; }; - // The `max_size` determines the maxmimum number of frames the buffer will + // The `max_size` determines the maximum 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, @@ -48,8 +47,10 @@ class FrameBuffer { ~FrameBuffer() = default; // Inserted frames may only reference backwards, and must have no duplicate - // references. - void InsertFrame(std::unique_ptr frame); + // references. Frame insertion will fail if `frame` is a duplicate, has + // already been decoded, invalid, or if the buffer is full and the frame is + // not a keyframe. Returns true if the frame was successfully inserted. + bool InsertFrame(std::unique_ptr frame); // Mark all frames belonging to the next decodable temporal unit as decoded // and returns them. diff --git a/api/video/frame_buffer_unittest.cc b/api/video/frame_buffer_unittest.cc index 41a486f192..92e2f67540 100644 --- a/api/video/frame_buffer_unittest.cc +++ b/api/video/frame_buffer_unittest.cc @@ -13,7 +13,6 @@ #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" @@ -35,14 +34,15 @@ TEST(FrameBuffer3Test, RejectInvalidRefs) { 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_FALSE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_FALSE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1, 1}).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(1)); } @@ -53,12 +53,13 @@ TEST(FrameBuffer3Test, LastContinuousUpdatesOnInsertedFrames) { EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(absl::nullopt)); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(absl::nullopt)); - buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + EXPECT_TRUE( + 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(2)); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); } @@ -68,13 +69,14 @@ TEST(FrameBuffer3Test, LastContinuousFrameReordering) { 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(3)); } @@ -83,10 +85,11 @@ TEST(FrameBuffer3Test, LastContinuousTemporalUnit) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, field_trials); - buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build()); + EXPECT_TRUE( + 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(2)); } @@ -95,14 +98,16 @@ TEST(FrameBuffer3Test, LastContinuousTemporalUnitReordering) { 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_TRUE( + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).Refs({1}).Build())); + EXPECT_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousTemporalUnitFrameId(), Eq(4)); } @@ -112,7 +117,8 @@ TEST(FrameBuffer3Test, NextDecodable) { field_trials); EXPECT_THAT(buffer.DecodableTemporalUnitsInfo(), Eq(absl::nullopt)); - buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(10U)); } @@ -121,10 +127,12 @@ TEST(FrameBuffer3Test, AdvanceNextDecodableOnExtraction) { 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).AsLast().Build())); + EXPECT_TRUE(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(), @@ -142,11 +150,12 @@ TEST(FrameBuffer3Test, AdvanceLastDecodableOnExtraction) { 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); + EXPECT_TRUE(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(), @@ -159,10 +168,12 @@ TEST(FrameBuffer3Test, FrameUpdatesNextDecodable) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, field_trials); - buffer.InsertFrame(test::FakeFrameBuilder().Time(20).Id(2).AsLast().Build()); + EXPECT_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); EXPECT_THAT(buffer.DecodableTemporalUnitsInfo()->next_rtp_timestamp, Eq(10U)); } @@ -170,23 +181,25 @@ 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({2}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({3}).AsLast().Build())); + EXPECT_TRUE(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_FALSE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(70).Id(7).AsLast().Build())); EXPECT_THAT(buffer.LastContinuousFrameId(), Eq(7)); } @@ -194,11 +207,12 @@ 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()); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build())); buffer.ExtractNextDecodableTemporalUnit(); buffer.DropNextDecodableTemporalUnit(); @@ -210,19 +224,20 @@ 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()); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(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_FALSE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_FALSE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(3))); } @@ -231,15 +246,17 @@ 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_TRUE( + buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(2).Refs({1}).Build())); + EXPECT_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(4).Refs({3}).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(4))); } @@ -248,15 +265,16 @@ 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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(3).Refs({1}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({2}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(50).Id(5).Refs({3}).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); @@ -269,15 +287,15 @@ TEST(FrameBuffer3Test, InterleavedStream) { EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(5))); - buffer.InsertFrame( - test::FakeFrameBuilder().Time(70).Id(7).Refs({5}).AsLast().Build()); + EXPECT_TRUE(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_FALSE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(90).Id(9).Refs({7}).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(9))); } @@ -289,12 +307,12 @@ TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, field_trials); - buffer.InsertFrame( - test::FakeFrameBuilder().Time(20).Id(3).AsLast().Build()); + EXPECT_TRUE(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_FALSE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(30).Id(2).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), IsEmpty()); } @@ -304,15 +322,15 @@ TEST(FrameBuffer3Test, LegacyFrameIdJumpBehavior) { FrameBuffer buffer(/*max_frame_slots=*/10, /*max_decode_history=*/100, field_trials); - buffer.InsertFrame( - test::FakeFrameBuilder().Time(20).Id(3).AsLast().Build()); + EXPECT_TRUE(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_FALSE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(1).AsLast().Build())); EXPECT_THAT(buffer.ExtractNextDecodableTemporalUnit(), ElementsAre(FrameWithId(1))); } @@ -324,20 +342,23 @@ TEST(FrameBuffer3Test, TotalNumberOfContinuousTemporalUnits) { field_trials); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(0)); - buffer.InsertFrame(test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build()); + EXPECT_TRUE(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_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({2}).Build())); + EXPECT_TRUE(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_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).Refs({2}).AsLast().Build())); EXPECT_THAT(buffer.GetTotalNumberOfContinuousTemporalUnits(), Eq(3)); } @@ -347,13 +368,16 @@ TEST(FrameBuffer3Test, TotalNumberOfDroppedFrames) { 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()); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(10).Id(1).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(2).Refs({1}).Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(20).Id(3).Refs({2}).AsLast().Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(4).Refs({1}).Build())); + EXPECT_TRUE(buffer.InsertFrame( + test::FakeFrameBuilder().Time(40).Id(5).Refs({4}).AsLast().Build())); buffer.ExtractNextDecodableTemporalUnit(); EXPECT_THAT(buffer.GetTotalNumberOfDroppedFrames(), Eq(0)); diff --git a/api/video/i010_buffer.cc b/api/video/i010_buffer.cc index b98e586562..d78e854eb9 100644 --- a/api/video/i010_buffer.cc +++ b/api/video/i010_buffer.cc @@ -11,9 +11,9 @@ #include +#include "api/make_ref_counted.h" #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/scale.h" @@ -66,12 +66,13 @@ rtc::scoped_refptr I010Buffer::Copy( const int width = source.width(); const int height = source.height(); rtc::scoped_refptr buffer = Create(width, height); - RTC_CHECK_EQ( - 0, libyuv::I010Copy( - 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)); + int res = libyuv::I010Copy( + 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); + RTC_DCHECK_EQ(res, 0); + return buffer; } @@ -81,12 +82,13 @@ rtc::scoped_refptr I010Buffer::Copy( const int width = source.width(); const int height = source.height(); rtc::scoped_refptr buffer = Create(width, height); - RTC_CHECK_EQ( - 0, libyuv::I420ToI010( - 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)); + int res = libyuv::I420ToI010( + 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); + RTC_DCHECK_EQ(res, 0); + return buffer; } @@ -109,52 +111,28 @@ rtc::scoped_refptr I010Buffer::Rotate( rtc::scoped_refptr buffer = Create(rotated_width, rotated_height); - // TODO(emircan): Remove this when there is libyuv::I010Rotate(). - for (int x = 0; x < src.width(); x++) { - for (int y = 0; y < src.height(); y++) { - int dest_x = x; - int dest_y = y; - switch (rotation) { - // This case is covered by the early return. - case webrtc::kVideoRotation_0: - RTC_DCHECK_NOTREACHED(); - break; - case webrtc::kVideoRotation_90: - dest_x = src.height() - y - 1; - dest_y = x; - break; - case webrtc::kVideoRotation_180: - dest_x = src.width() - x - 1; - dest_y = src.height() - y - 1; - break; - case webrtc::kVideoRotation_270: - dest_x = y; - dest_y = src.width() - x - 1; - break; - } - buffer->MutableDataY()[dest_x + buffer->StrideY() * dest_y] = - src.DataY()[x + src.StrideY() * y]; - dest_x /= 2; - dest_y /= 2; - int src_x = x / 2; - int src_y = y / 2; - buffer->MutableDataU()[dest_x + buffer->StrideU() * dest_y] = - src.DataU()[src_x + src.StrideU() * src_y]; - buffer->MutableDataV()[dest_x + buffer->StrideV() * dest_y] = - src.DataV()[src_x + src.StrideV() * src_y]; - } - } + + int res = libyuv::I010Rotate( + 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)); + RTC_DCHECK_EQ(res, 0); + return buffer; } rtc::scoped_refptr I010Buffer::ToI420() { rtc::scoped_refptr i420_buffer = I420Buffer::Create(width(), height()); - libyuv::I010ToI420(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()); + int res = libyuv::I010ToI420( + 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()); + RTC_DCHECK_EQ(res, 0); + return i420_buffer; } diff --git a/api/video/i210_buffer.cc b/api/video/i210_buffer.cc new file mode 100644 index 0000000000..c83c8a0c0b --- /dev/null +++ b/api/video/i210_buffer.cc @@ -0,0 +1,211 @@ +/* + * 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/i210_buffer.h" + +#include + +#include "api/make_ref_counted.h" +#include "api/video/i420_buffer.h" +#include "api/video/i422_buffer.h" +#include "rtc_base/checks.h" +#include "third_party/libyuv/include/libyuv/convert.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; +static const int kBytesPerPixel = 2; + +namespace webrtc { + +namespace { + +int I210DataSize(int height, int stride_y, int stride_u, int stride_v) { + return kBytesPerPixel * + (stride_y * height + stride_u * height + stride_v * height); +} + +} // namespace + +I210Buffer::I210Buffer(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(I210DataSize(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); +} + +I210Buffer::~I210Buffer() {} + +// static +rtc::scoped_refptr I210Buffer::Create(int width, int height) { + return rtc::make_ref_counted(width, height, width, + (width + 1) / 2, (width + 1) / 2); +} + +// static +rtc::scoped_refptr I210Buffer::Copy( + const I210BufferInterface& source) { + const int width = source.width(); + const int height = source.height(); + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ( + 0, libyuv::I210Copy( + 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 I210Buffer::Copy( + const I420BufferInterface& source) { + const int width = source.width(); + const int height = source.height(); + auto i422buffer = I422Buffer::Copy(source); + rtc::scoped_refptr buffer = Create(width, height); + RTC_CHECK_EQ(0, libyuv::I422ToI210(i422buffer->DataY(), i422buffer->StrideY(), + i422buffer->DataU(), i422buffer->StrideU(), + i422buffer->DataV(), i422buffer->StrideV(), + buffer->MutableDataY(), buffer->StrideY(), + buffer->MutableDataU(), buffer->StrideU(), + buffer->MutableDataV(), buffer->StrideV(), + width, height)); + return buffer; +} + +// static +rtc::scoped_refptr I210Buffer::Rotate( + const I210BufferInterface& 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 = + I210Buffer::Create(rotated_width, rotated_height); + + RTC_CHECK_EQ(0, + libyuv::I210Rotate( + 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 I210Buffer::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + libyuv::I210ToI420(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; +} + +int I210Buffer::width() const { + return width_; +} + +int I210Buffer::height() const { + return height_; +} + +const uint16_t* I210Buffer::DataY() const { + return data_.get(); +} +const uint16_t* I210Buffer::DataU() const { + return data_.get() + stride_y_ * height_; +} +const uint16_t* I210Buffer::DataV() const { + return data_.get() + stride_y_ * height_ + stride_u_ * height_; +} + +int I210Buffer::StrideY() const { + return stride_y_; +} +int I210Buffer::StrideU() const { + return stride_u_; +} +int I210Buffer::StrideV() const { + return stride_v_; +} + +uint16_t* I210Buffer::MutableDataY() { + return const_cast(DataY()); +} +uint16_t* I210Buffer::MutableDataU() { + return const_cast(DataU()); +} +uint16_t* I210Buffer::MutableDataV() { + return const_cast(DataV()); +} + +void I210Buffer::CropAndScaleFrom(const I210BufferInterface& 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); + RTC_CHECK_GE(crop_width, 0); + RTC_CHECK_GE(crop_height, 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 uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; + const uint16_t* u_plane = + src.DataU() + src.StrideU() * uv_offset_y + uv_offset_x; + const uint16_t* v_plane = + src.DataV() + src.StrideV() * uv_offset_y + uv_offset_x; + int res = libyuv::I422Scale_16( + 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); +} + +void I210Buffer::ScaleFrom(const I210BufferInterface& src) { + CropAndScaleFrom(src, 0, 0, src.width(), src.height()); +} + +} // namespace webrtc diff --git a/api/video/i210_buffer.h b/api/video/i210_buffer.h new file mode 100644 index 0000000000..e3b6452b95 --- /dev/null +++ b/api/video/i210_buffer.h @@ -0,0 +1,84 @@ +/* + * 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_I210_BUFFER_H_ +#define API_VIDEO_I210_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" + +namespace webrtc { + +// Plain I210 (yuv 422 planar 10 bits) buffer in standard memory. +class I210Buffer : public I210BufferInterface { + public: + // Create a new buffer. + static rtc::scoped_refptr Create(int width, int height); + + // Create a new buffer and copy the pixel data. + static rtc::scoped_refptr Copy(const I210BufferInterface& buffer); + + // Convert and put I420 buffer into a new buffer. + static rtc::scoped_refptr Copy(const I420BufferInterface& buffer); + + // Return a rotated copy of `src`. + static rtc::scoped_refptr Rotate(const I210BufferInterface& src, + VideoRotation rotation); + + // VideoFrameBuffer implementation. + rtc::scoped_refptr ToI420() override; + + // PlanarYuv16BBuffer implementation. + int width() const override; + int height() const override; + const uint16_t* DataY() const override; + const uint16_t* DataU() const override; + const uint16_t* DataV() const override; + int StrideY() const override; + int StrideU() const override; + int StrideV() const override; + + uint16_t* MutableDataY(); + uint16_t* MutableDataU(); + uint16_t* MutableDataV(); + + // Scale the cropped area of `src` to the size of `this` buffer, and + // write the result into `this`. + void CropAndScaleFrom(const I210BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height); + + // Scale all of `src` to the size of `this` buffer, with no cropping. + void ScaleFrom(const I210BufferInterface& src); + + protected: + I210Buffer(int width, int height, int stride_y, int stride_u, int stride_v); + ~I210Buffer() 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_I210_BUFFER_H_ diff --git a/api/video/i410_buffer.cc b/api/video/i410_buffer.cc new file mode 100644 index 0000000000..1b0d4fdb5c --- /dev/null +++ b/api/video/i410_buffer.cc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023 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/i410_buffer.h" + +#include + +#include +#include + +#include "api/make_ref_counted.h" +#include "api/video/i420_buffer.h" +#include "rtc_base/checks.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; +static const int kBytesPerPixel = 2; + +namespace webrtc { + +namespace { + +int I410DataSize(int height, int stride_y, int stride_u, int stride_v) { + return kBytesPerPixel * + (stride_y * height + stride_u * height + stride_v * height); +} + +} // namespace + +I410Buffer::I410Buffer(int width, int height) + : I410Buffer(width, height, width, width, width) {} + +I410Buffer::I410Buffer(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(I410DataSize(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); + RTC_DCHECK_GE(stride_v, width); +} + +I410Buffer::~I410Buffer() {} + +// static +rtc::scoped_refptr I410Buffer::Create(int width, int height) { + return rtc::make_ref_counted(width, height); +} + +// static +rtc::scoped_refptr I410Buffer::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 I410Buffer::Copy( + const I410BufferInterface& source) { + return Copy(source.width(), source.height(), source.DataY(), source.StrideY(), + source.DataU(), source.StrideU(), source.DataV(), + source.StrideV()); +} + +// static +rtc::scoped_refptr I410Buffer::Copy(int width, + int height, + const uint16_t* data_y, + int stride_y, + const uint16_t* data_u, + int stride_u, + const uint16_t* data_v, + int stride_v) { + // Note: May use different strides than the input data. + rtc::scoped_refptr buffer = Create(width, height); + int res = libyuv::I410Copy(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); + RTC_DCHECK_EQ(res, 0); + + return buffer; +} + +// static +rtc::scoped_refptr I410Buffer::Rotate( + const I410BufferInterface& 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 = + I410Buffer::Create(rotated_width, rotated_height); + + int res = libyuv::I410Rotate( + 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)); + RTC_DCHECK_EQ(res, 0); + + return buffer; +} + +rtc::scoped_refptr I410Buffer::ToI420() { + rtc::scoped_refptr i420_buffer = + I420Buffer::Create(width(), height()); + int res = libyuv::I410ToI420( + 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()); + RTC_DCHECK_EQ(res, 0); + + return i420_buffer; +} + +void I410Buffer::InitializeData() { + memset(data_.get(), 0, + I410DataSize(height_, stride_y_, stride_u_, stride_v_)); +} + +int I410Buffer::width() const { + return width_; +} + +int I410Buffer::height() const { + return height_; +} + +const uint16_t* I410Buffer::DataY() const { + return data_.get(); +} +const uint16_t* I410Buffer::DataU() const { + return data_.get() + stride_y_ * height_; +} +const uint16_t* I410Buffer::DataV() const { + return data_.get() + stride_y_ * height_ + stride_u_ * height_; +} + +int I410Buffer::StrideY() const { + return stride_y_; +} +int I410Buffer::StrideU() const { + return stride_u_; +} +int I410Buffer::StrideV() const { + return stride_v_; +} + +uint16_t* I410Buffer::MutableDataY() { + return const_cast(DataY()); +} +uint16_t* I410Buffer::MutableDataU() { + return const_cast(DataU()); +} +uint16_t* I410Buffer::MutableDataV() { + return const_cast(DataV()); +} + +void I410Buffer::CropAndScaleFrom(const I410BufferInterface& 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); + + const uint16_t* y_plane = src.DataY() + src.StrideY() * offset_y + offset_x; + const uint16_t* u_plane = src.DataU() + src.StrideU() * offset_y + offset_x; + const uint16_t* v_plane = src.DataV() + src.StrideV() * offset_y + offset_x; + int res = libyuv::I444Scale_16( + 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); +} + +void I410Buffer::ScaleFrom(const I410BufferInterface& src) { + CropAndScaleFrom(src, 0, 0, src.width(), src.height()); +} + +} // namespace webrtc diff --git a/api/video/i410_buffer.h b/api/video/i410_buffer.h new file mode 100644 index 0000000000..1c0cd86c12 --- /dev/null +++ b/api/video/i410_buffer.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 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_I410_BUFFER_H_ +#define API_VIDEO_I410_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" + +namespace webrtc { + +// Plain I410 (yuv 444 planar 10 bits) buffer in standard memory. +class RTC_EXPORT I410Buffer : public I410BufferInterface { + 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 I410BufferInterface& buffer); + + static rtc::scoped_refptr Copy(int width, + int height, + const uint16_t* data_y, + int stride_y, + const uint16_t* data_u, + int stride_u, + const uint16_t* data_v, + int stride_v); + + // Returns a rotated copy of |src|. + static rtc::scoped_refptr Rotate(const I410BufferInterface& src, + VideoRotation rotation); + + rtc::scoped_refptr ToI420() final; + const I420BufferInterface* GetI420() const final { return nullptr; } + + // 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(https://crbug.com/390941): 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 uint16_t* DataY() const override; + const uint16_t* DataU() const override; + const uint16_t* DataV() const override; + + int StrideY() const override; + int StrideU() const override; + int StrideV() const override; + + uint16_t* MutableDataY(); + uint16_t* MutableDataU(); + uint16_t* MutableDataV(); + + // Scale the cropped area of |src| to the size of |this| buffer, and + // write the result into |this|. + void CropAndScaleFrom(const I410BufferInterface& src, + int offset_x, + int offset_y, + int crop_width, + int crop_height); + + // Scale all of `src` to the size of `this` buffer, with no cropping. + void ScaleFrom(const I410BufferInterface& src); + + protected: + I410Buffer(int width, int height); + I410Buffer(int width, int height, int stride_y, int stride_u, int stride_v); + + ~I410Buffer() 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_I410_BUFFER_H_ diff --git a/api/video/i420_buffer.cc b/api/video/i420_buffer.cc index deecf1d71d..bf7fc06ee9 100644 --- a/api/video/i420_buffer.cc +++ b/api/video/i420_buffer.cc @@ -14,8 +14,8 @@ #include #include +#include "api/make_ref_counted.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" diff --git a/api/video/i420_buffer.h b/api/video/i420_buffer.h index af52c64fb4..b337489657 100644 --- a/api/video/i420_buffer.h +++ b/api/video/i420_buffer.h @@ -65,8 +65,8 @@ class RTC_EXPORT I420Buffer : public I420BufferInterface { // 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. + // TODO(https://crbug.com/390941): 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; diff --git a/api/video/i422_buffer.cc b/api/video/i422_buffer.cc index d6cf0d6c97..fddc1b57fd 100644 --- a/api/video/i422_buffer.cc +++ b/api/video/i422_buffer.cc @@ -14,9 +14,9 @@ #include #include +#include "api/make_ref_counted.h" #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" @@ -31,129 +31,6 @@ 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) @@ -210,12 +87,13 @@ rtc::scoped_refptr I422Buffer::Copy( 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)); + int res = 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); + RTC_DCHECK_EQ(res, 0); + return buffer; } @@ -230,11 +108,13 @@ rtc::scoped_refptr I422Buffer::Copy(int width, 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)); + int res = 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); + RTC_DCHECK_EQ(res, 0); + return buffer; } @@ -256,13 +136,13 @@ rtc::scoped_refptr I422Buffer::Rotate( 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))); + int res = libyuv::I422Rotate( + 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)); + RTC_DCHECK_EQ(res, 0); return buffer; } @@ -270,11 +150,13 @@ rtc::scoped_refptr I422Buffer::Rotate( 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()); + int res = 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()); + RTC_DCHECK_EQ(res, 0); + return i420_buffer; } @@ -343,12 +225,12 @@ void I422Buffer::CropAndScaleFrom(const I422BufferInterface& src, 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); + int res = + libyuv::I422Scale(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); } diff --git a/api/video/i422_buffer.h b/api/video/i422_buffer.h index 16c717469b..600b4ecea7 100644 --- a/api/video/i422_buffer.h +++ b/api/video/i422_buffer.h @@ -61,8 +61,8 @@ class RTC_EXPORT I422Buffer : public I422BufferInterface { // 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. + // TODO(https://crbug.com/390941): 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; diff --git a/api/video/i444_buffer.cc b/api/video/i444_buffer.cc index 8bf9f76625..98e892308f 100644 --- a/api/video/i444_buffer.cc +++ b/api/video/i444_buffer.cc @@ -14,9 +14,9 @@ #include #include +#include "api/make_ref_counted.h" #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" diff --git a/api/video/i444_buffer.h b/api/video/i444_buffer.h index 557bf4f3e0..f1e3f63114 100644 --- a/api/video/i444_buffer.h +++ b/api/video/i444_buffer.h @@ -58,8 +58,8 @@ class RTC_EXPORT I444Buffer : public I444BufferInterface { // 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. + // TODO(https://crbug.com/390941): 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; diff --git a/api/video/nv12_buffer.cc b/api/video/nv12_buffer.cc index 37d688b88b..ca9dcd8677 100644 --- a/api/video/nv12_buffer.cc +++ b/api/video/nv12_buffer.cc @@ -10,9 +10,9 @@ #include "api/video/nv12_buffer.h" +#include "api/make_ref_counted.h" #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/scale.h" diff --git a/api/video/nv12_buffer.h b/api/video/nv12_buffer.h index 7baef2aeba..46a85f82e1 100644 --- a/api/video/nv12_buffer.h +++ b/api/video/nv12_buffer.h @@ -52,8 +52,8 @@ class RTC_EXPORT NV12Buffer : public NV12BufferInterface { // 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. + // TODO(https://crbug.com/390941): Deprecated. Should be deleted if/when those + // issues are resolved in a better way. Or in the mean time, use SetBlack. void InitializeData(); // Scale the cropped area of `src` to the size of `this` buffer, and diff --git a/api/video/recordable_encoded_frame.h b/api/video/recordable_encoded_frame.h index b4ad83a344..47ea23f119 100644 --- a/api/video/recordable_encoded_frame.h +++ b/api/video/recordable_encoded_frame.h @@ -17,7 +17,6 @@ #include "api/video/color_space.h" #include "api/video/encoded_image.h" #include "api/video/video_codec_type.h" -#include "rtc_base/ref_count.h" namespace webrtc { @@ -25,6 +24,7 @@ namespace webrtc { class RecordableEncodedFrame { public: // Encoded resolution in pixels + // TODO(bugs.webrtc.org/12114) : remove in favor of Resolution. struct EncodedResolution { bool empty() const { return width == 0 && height == 0; } diff --git a/api/video/render_resolution.h b/api/video/render_resolution.h index edcf8f8ee5..fcf4f122d6 100644 --- a/api/video/render_resolution.h +++ b/api/video/render_resolution.h @@ -13,6 +13,7 @@ namespace webrtc { +// TODO(bugs.webrtc.org/12114) : remove in favor of Resolution. class RenderResolution { public: constexpr RenderResolution() = default; diff --git a/api/video/resolution.h b/api/video/resolution.h new file mode 100644 index 0000000000..11ffef0b03 --- /dev/null +++ b/api/video/resolution.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_RESOLUTION_H_ +#define API_VIDEO_RESOLUTION_H_ + +#include + +namespace webrtc { + +// A struct representing a video resolution in pixels. +struct Resolution { + int width = 0; + int height = 0; + + // Helper methods. + int PixelCount() const { return width * height; } + std::pair ToPair() const { return std::make_pair(width, height); } +}; + +inline bool operator==(const Resolution& lhs, const Resolution& rhs) { + return lhs.width == rhs.width && lhs.height == rhs.height; +} + +inline bool operator!=(const Resolution& lhs, const Resolution& rhs) { + return !(lhs == rhs); +} + +} // namespace webrtc + +#endif // API_VIDEO_RESOLUTION_H_ diff --git a/api/video/rtp_video_frame_assembler.cc b/api/video/rtp_video_frame_assembler.cc index 81c08389bb..3d041ca218 100644 --- a/api/video/rtp_video_frame_assembler.cc +++ b/api/video/rtp_video_frame_assembler.cc @@ -19,6 +19,7 @@ #include "absl/container/inlined_vector.h" #include "absl/types/optional.h" +#include "modules/rtp_rtcp/source/frame_object.h" #include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_generic_frame_descriptor_extension.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" @@ -28,10 +29,10 @@ #include "modules/rtp_rtcp/source/video_rtp_depacketizer_raw.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h" #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp9.h" -#include "modules/video_coding/frame_object.h" #include "modules/video_coding/packet_buffer.h" #include "modules/video_coding/rtp_frame_reference_finder.h" #include "rtc_base/logging.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" namespace webrtc { namespace { diff --git a/api/video/test/BUILD.gn b/api/video/test/BUILD.gn index 5d654cff1a..60ec4b852f 100644 --- a/api/video/test/BUILD.gn +++ b/api/video/test/BUILD.gn @@ -12,6 +12,8 @@ rtc_library("rtc_api_video_unittests") { testonly = true sources = [ "color_space_unittest.cc", + "i210_buffer_unittest.cc", + "i410_buffer_unittest.cc", "i422_buffer_unittest.cc", "i444_buffer_unittest.cc", "nv12_buffer_unittest.cc", @@ -22,6 +24,7 @@ rtc_library("rtc_api_video_unittests") { "..:video_adaptation", "..:video_bitrate_allocation", "..:video_frame", + "..:video_frame_i010", "..:video_rtp_headers", "../../../test:frame_utils", "../../../test:test_support", diff --git a/api/video/test/i210_buffer_unittest.cc b/api/video/test/i210_buffer_unittest.cc new file mode 100644 index 0000000000..aaa231b6d2 --- /dev/null +++ b/api/video/test/i210_buffer_unittest.cc @@ -0,0 +1,126 @@ + +/* + * 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/i210_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 FillI210Buffer(rtc::scoped_refptr buf) { + const uint16_t Y = 4; + const uint16_t U = 8; + const uint16_t V = 16; + 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(I210BufferTest, 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 i210_buffer(I210Buffer::Create(width, height)); + EXPECT_EQ(width, i210_buffer->width()); + EXPECT_EQ(height, i210_buffer->height()); + EXPECT_EQ(stride, i210_buffer->StrideY()); + EXPECT_EQ(halfstride, i210_buffer->StrideU()); + EXPECT_EQ(halfstride, i210_buffer->StrideV()); + EXPECT_EQ(halfwidth, i210_buffer->ChromaWidth()); + EXPECT_EQ(height, i210_buffer->ChromaHeight()); +} + +TEST(I210BufferTest, ReadPixels) { + constexpr int width = 3; + constexpr int halfwidth = (width + 1) >> 1; + constexpr int height = 3; + + rtc::scoped_refptr i210_buffer(I210Buffer::Create(width, height)); + // Y = 4, U = 8, V = 16. + FillI210Buffer(i210_buffer); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(4, GetY(i210_buffer, col, row)); + } + } + for (int row = 0; row < height; row++) { + for (int col = 0; col < halfwidth; col++) { + EXPECT_EQ(8, GetU(i210_buffer, col, row)); + EXPECT_EQ(16, GetV(i210_buffer, col, row)); + } + } +} + +TEST(I210BufferTest, ToI420) { + constexpr int width = 3; + constexpr int halfwidth = (width + 1) >> 1; + constexpr int height = 3; + constexpr int size = width * height; + constexpr int quartersize = (width + 1) / 2 * (height + 1) / 2; + rtc::scoped_refptr reference(I420Buffer::Create(width, height)); + memset(reference->MutableDataY(), 1, size); + memset(reference->MutableDataU(), 2, quartersize); + memset(reference->MutableDataV(), 4, quartersize); + + rtc::scoped_refptr i210_buffer(I210Buffer::Create(width, height)); + // Y = 4, U = 8, V = 16. + FillI210Buffer(i210_buffer); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(4, GetY(i210_buffer, col, row)); + } + } + for (int row = 0; row < height; row++) { + for (int col = 0; col < halfwidth; col++) { + EXPECT_EQ(8, GetU(i210_buffer, col, row)); + EXPECT_EQ(16, GetV(i210_buffer, col, row)); + } + } + + rtc::scoped_refptr i420_buffer(i210_buffer->ToI420()); + EXPECT_TRUE(test::FrameBufsEqual(reference, i420_buffer)); + EXPECT_EQ(height, i420_buffer->height()); + EXPECT_EQ(width, i420_buffer->width()); +} + +} // namespace webrtc diff --git a/api/video/test/i410_buffer_unittest.cc b/api/video/test/i410_buffer_unittest.cc new file mode 100644 index 0000000000..c5d2d5bf2d --- /dev/null +++ b/api/video/test/i410_buffer_unittest.cc @@ -0,0 +1,120 @@ + +/* + * Copyright (c) 2023 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/i410_buffer.h" + +#include "api/video/i420_buffer.h" +#include "test/frame_utils.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { +constexpr uint16_t kYValue = 4; +constexpr uint16_t kUValue = 8; +constexpr uint16_t kVValue = 16; + +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 FillI410Buffer(rtc::scoped_refptr buf) { + for (int row = 0; row < buf->height(); ++row) { + for (int col = 0; col < buf->width(); ++col) { + buf->MutableDataY()[row * buf->StrideY() + col] = kYValue; + buf->MutableDataU()[row * buf->StrideU() + col] = kUValue; + buf->MutableDataV()[row * buf->StrideV() + col] = kVValue; + } + } +} + +} // namespace + +TEST(I410BufferTest, InitialData) { + constexpr int stride = 3; + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i410_buffer(I410Buffer::Create(width, height)); + EXPECT_EQ(width, i410_buffer->width()); + EXPECT_EQ(height, i410_buffer->height()); + EXPECT_EQ(stride, i410_buffer->StrideY()); + EXPECT_EQ(stride, i410_buffer->StrideU()); + EXPECT_EQ(stride, i410_buffer->StrideV()); + EXPECT_EQ(3, i410_buffer->ChromaWidth()); + EXPECT_EQ(3, i410_buffer->ChromaHeight()); +} + +TEST(I410BufferTest, ReadPixels) { + constexpr int width = 3; + constexpr int height = 3; + + rtc::scoped_refptr i410_buffer(I410Buffer::Create(width, height)); + FillI410Buffer(i410_buffer); + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(kYValue, GetY(i410_buffer, col, row)); + EXPECT_EQ(kUValue, GetU(i410_buffer, col, row)); + EXPECT_EQ(kVValue, GetV(i410_buffer, col, row)); + } + } +} + +TEST(I410BufferTest, ToI420) { + // libyuv I410ToI420 only handles correctly even sizes and skips last row/col + // if odd. + constexpr int width = 4; + constexpr int height = 4; + constexpr int size_y = width * height; + constexpr int size_u = (width + 1) / 2 * (height + 1) / 2; + constexpr int size_v = (width + 1) / 2 * (height + 1) / 2; + rtc::scoped_refptr reference(I420Buffer::Create(width, height)); + // I410 is 10-bit while I420 is 8 bit, so last 2 bits would be discarded. + memset(reference->MutableDataY(), kYValue >> 2, size_y); + memset(reference->MutableDataU(), kUValue >> 2, size_u); + memset(reference->MutableDataV(), kVValue >> 2, size_v); + + rtc::scoped_refptr i410_buffer(I410Buffer::Create(width, height)); + FillI410Buffer(i410_buffer); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(kYValue, GetY(i410_buffer, col, row)); + EXPECT_EQ(kUValue, GetU(i410_buffer, col, row)); + EXPECT_EQ(kVValue, GetV(i410_buffer, col, row)); + } + } + + rtc::scoped_refptr i420_buffer(i410_buffer->ToI420()); + + // Confirm YUV values are as expected. + for (int row = 0; row < height; row++) { + for (int col = 0; col < width; col++) { + EXPECT_EQ(1, i420_buffer->DataY()[row * i420_buffer->StrideY() + col]); + } + } + + 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/video_frame.cc b/api/video/video_frame.cc index d97e3aa82a..35dedce1b2 100644 --- a/api/video/video_frame.cc +++ b/api/video/video_frame.cc @@ -163,8 +163,9 @@ VideoFrame::Builder::~Builder() = default; VideoFrame VideoFrame::Builder::build() { RTC_CHECK(video_frame_buffer_ != nullptr); - return VideoFrame(id_, video_frame_buffer_, timestamp_us_, timestamp_rtp_, - ntp_time_ms_, rotation_, color_space_, update_rect_, + return VideoFrame(id_, video_frame_buffer_, timestamp_us_, + capture_time_identifier_, timestamp_rtp_, ntp_time_ms_, + rotation_, color_space_, render_parameters_, update_rect_, packet_infos_); } @@ -186,6 +187,12 @@ VideoFrame::Builder& VideoFrame::Builder::set_timestamp_us( return *this; } +VideoFrame::Builder& VideoFrame::Builder::set_capture_time_identifier( + const absl::optional& capture_time_identifier) { + capture_time_identifier_ = capture_time_identifier; + return *this; +} + VideoFrame::Builder& VideoFrame::Builder::set_timestamp_rtp( uint32_t timestamp_rtp) { timestamp_rtp_ = timestamp_rtp; @@ -256,10 +263,12 @@ VideoFrame::VideoFrame(const rtc::scoped_refptr& buffer, VideoFrame::VideoFrame(uint16_t id, const rtc::scoped_refptr& buffer, int64_t timestamp_us, + const absl::optional& capture_time_identifier, uint32_t timestamp_rtp, int64_t ntp_time_ms, VideoRotation rotation, const absl::optional& color_space, + const RenderParameters& render_parameters, const absl::optional& update_rect, RtpPacketInfos packet_infos) : id_(id), @@ -267,8 +276,10 @@ VideoFrame::VideoFrame(uint16_t id, timestamp_rtp_(timestamp_rtp), ntp_time_ms_(ntp_time_ms), timestamp_us_(timestamp_us), + capture_time_identifier_(capture_time_identifier), rotation_(rotation), color_space_(color_space), + render_parameters_(render_parameters), update_rect_(update_rect), packet_infos_(std::move(packet_infos)) { if (update_rect_) { diff --git a/api/video/video_frame.h b/api/video/video_frame.h index 512055d770..a257a3209e 100644 --- a/api/video/video_frame.h +++ b/api/video/video_frame.h @@ -29,6 +29,9 @@ namespace webrtc { class RTC_EXPORT VideoFrame { public: + // Value used to signal that `VideoFrame::id()` is not set. + static constexpr uint16_t kNotSetId = 0; + struct RTC_EXPORT UpdateRect { int offset_x; int offset_y; @@ -78,6 +81,21 @@ class RTC_EXPORT VideoFrame { Timestamp finish; }; + struct RTC_EXPORT RenderParameters { + bool use_low_latency_rendering = false; + absl::optional max_composition_delay_in_frames; + + bool operator==(const RenderParameters& other) const { + return other.use_low_latency_rendering == use_low_latency_rendering && + other.max_composition_delay_in_frames == + max_composition_delay_in_frames; + } + + bool operator!=(const RenderParameters& other) const { + return !(*this == other); + } + }; + // Preferred way of building VideoFrame objects. class RTC_EXPORT Builder { public: @@ -89,6 +107,8 @@ class RTC_EXPORT VideoFrame { const rtc::scoped_refptr& buffer); Builder& set_timestamp_ms(int64_t timestamp_ms); Builder& set_timestamp_us(int64_t timestamp_us); + Builder& set_capture_time_identifier( + const absl::optional& capture_time_identifier); Builder& set_timestamp_rtp(uint32_t timestamp_rtp); Builder& set_ntp_time_ms(int64_t ntp_time_ms); Builder& set_rotation(VideoRotation rotation); @@ -99,13 +119,15 @@ class RTC_EXPORT VideoFrame { Builder& set_packet_infos(RtpPacketInfos packet_infos); private: - uint16_t id_ = 0; + uint16_t id_ = kNotSetId; rtc::scoped_refptr video_frame_buffer_; int64_t timestamp_us_ = 0; + absl::optional capture_time_identifier_; uint32_t timestamp_rtp_ = 0; int64_t ntp_time_ms_ = 0; VideoRotation rotation_ = kVideoRotation_0; absl::optional color_space_; + RenderParameters render_parameters_; absl::optional update_rect_; RtpPacketInfos packet_infos_; }; @@ -134,12 +156,12 @@ class RTC_EXPORT VideoFrame { // Get frame size in pixels. uint32_t size() const; - // Get frame ID. Returns 0 if ID is not set. Not guaranteed to be transferred - // from the sender to the receiver, but preserved on the sender side. The id - // should be propagated between all frame modifications during its lifetime - // from capturing to sending as encoded image. It is intended to be unique - // over a time window of a few minutes for the peer connection to which the - // corresponding video stream belongs to. + // Get frame ID. Returns `kNotSetId` if ID is not set. Not guaranteed to be + // transferred from the sender to the receiver, but preserved on the sender + // side. The id should be propagated between all frame modifications during + // its lifetime from capturing to sending as encoded image. It is intended to + // be unique over a time window of a few minutes for the peer connection to + // which the corresponding video stream belongs to. uint16_t id() const { return id_; } void set_id(uint16_t id) { id_ = id; } @@ -147,9 +169,13 @@ class RTC_EXPORT VideoFrame { int64_t timestamp_us() const { return timestamp_us_; } void set_timestamp_us(int64_t timestamp_us) { timestamp_us_ = timestamp_us; } - // TODO(nisse): After the cricket::VideoFrame and webrtc::VideoFrame - // merge, timestamps other than timestamp_us will likely be - // deprecated. + const absl::optional& capture_time_identifier() const { + return capture_time_identifier_; + } + void set_capture_time_identifier( + const absl::optional& capture_time_identifier) { + capture_time_identifier_ = capture_time_identifier; + } // Set frame timestamp (90kHz). void set_timestamp(uint32_t timestamp) { timestamp_rtp_ = timestamp; } @@ -157,10 +183,6 @@ class RTC_EXPORT VideoFrame { // Get frame timestamp (90kHz). uint32_t timestamp() const { return timestamp_rtp_; } - // For now, transport_frame_id and rtp timestamp are the same. - // TODO(nisse): Must be handled differently for QUIC. - uint32_t transport_frame_id() const { return timestamp(); } - // Set capture ntp time in milliseconds. void set_ntp_time_ms(int64_t ntp_time_ms) { ntp_time_ms_ = ntp_time_ms; } @@ -186,18 +208,20 @@ class RTC_EXPORT VideoFrame { color_space_ = color_space; } - // max_composition_delay_in_frames() is used in an experiment of a low-latency - // renderer algorithm see crbug.com/1138888. - absl::optional max_composition_delay_in_frames() const { - return max_composition_delay_in_frames_; + RenderParameters render_parameters() const { return render_parameters_; } + void set_render_parameters(const RenderParameters& render_parameters) { + render_parameters_ = render_parameters; } - void set_max_composition_delay_in_frames( - absl::optional max_composition_delay_in_frames) { - max_composition_delay_in_frames_ = max_composition_delay_in_frames; + + // Deprecated in favor of render_parameters, will be removed once Chromium is + // updated. max_composition_delay_in_frames() is used in an experiment of a + // low-latency renderer algorithm see crbug.com/1138888. + [[deprecated("Use render_parameters() instead.")]] absl::optional + max_composition_delay_in_frames() const { + return render_parameters_.max_composition_delay_in_frames; } // Get render time in milliseconds. - // TODO(nisse): Deprecated. Migrate all users to timestamp_us(). int64_t render_time_ms() const; // Return the underlying buffer. Never nullptr for a properly @@ -207,7 +231,6 @@ class RTC_EXPORT VideoFrame { void set_video_frame_buffer( const rtc::scoped_refptr& buffer); - // TODO(nisse): Deprecated. // Return true if the frame is stored in a texture. bool is_texture() const { return video_frame_buffer()->type() == VideoFrameBuffer::Type::kNative; @@ -250,10 +273,12 @@ class RTC_EXPORT VideoFrame { VideoFrame(uint16_t id, const rtc::scoped_refptr& buffer, int64_t timestamp_us, + const absl::optional& capture_time_identifier, uint32_t timestamp_rtp, int64_t ntp_time_ms, VideoRotation rotation, const absl::optional& color_space, + const RenderParameters& render_parameters, const absl::optional& update_rect, RtpPacketInfos packet_infos); @@ -263,9 +288,11 @@ class RTC_EXPORT VideoFrame { uint32_t timestamp_rtp_; int64_t ntp_time_ms_; int64_t timestamp_us_; + absl::optional capture_time_identifier_; VideoRotation rotation_; absl::optional color_space_; - absl::optional max_composition_delay_in_frames_; + // Contains parameters that affect have the frame should be rendered. + RenderParameters render_parameters_; // Updated since the last frame area. If present it means that the bounding // box of all the changes is within the rectangular area and is close to it. // If absent, it means that there's no information about the change at all and diff --git a/api/video/video_frame_buffer.cc b/api/video/video_frame_buffer.cc index 25bca9ab19..374b438adc 100644 --- a/api/video/video_frame_buffer.cc +++ b/api/video/video_frame_buffer.cc @@ -58,6 +58,16 @@ const I010BufferInterface* VideoFrameBuffer::GetI010() const { return static_cast(this); } +const I210BufferInterface* VideoFrameBuffer::GetI210() const { + RTC_CHECK(type() == Type::kI210); + return static_cast(this); +} + +const I410BufferInterface* VideoFrameBuffer::GetI410() const { + RTC_CHECK(type() == Type::kI410); + return static_cast(this); +} + const NV12BufferInterface* VideoFrameBuffer::GetNV12() const { RTC_CHECK(type() == Type::kNV12); return static_cast(this); @@ -87,6 +97,10 @@ const char* VideoFrameBufferTypeToString(VideoFrameBuffer::Type type) { return "kI422"; case VideoFrameBuffer::Type::kI010: return "kI010"; + case VideoFrameBuffer::Type::kI210: + return "kI210"; + case VideoFrameBuffer::Type::kI410: + return "kI410"; case VideoFrameBuffer::Type::kNV12: return "kNV12"; default: @@ -176,6 +190,30 @@ int I010BufferInterface::ChromaHeight() const { return (height() + 1) / 2; } +VideoFrameBuffer::Type I210BufferInterface::type() const { + return Type::kI210; +} + +int I210BufferInterface::ChromaWidth() const { + return (width() + 1) / 2; +} + +int I210BufferInterface::ChromaHeight() const { + return height(); +} + +VideoFrameBuffer::Type I410BufferInterface::type() const { + return Type::kI410; +} + +int I410BufferInterface::ChromaWidth() const { + return width(); +} + +int I410BufferInterface::ChromaHeight() const { + return height(); +} + VideoFrameBuffer::Type NV12BufferInterface::type() const { return Type::kNV12; } diff --git a/api/video/video_frame_buffer.h b/api/video/video_frame_buffer.h index 23a40bebfc..aaf786699f 100644 --- a/api/video/video_frame_buffer.h +++ b/api/video/video_frame_buffer.h @@ -25,6 +25,8 @@ class I420ABufferInterface; class I422BufferInterface; class I444BufferInterface; class I010BufferInterface; +class I210BufferInterface; +class I410BufferInterface; class NV12BufferInterface; // Base class for frame buffers of different types of pixel format and storage. @@ -56,6 +58,8 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface { kI422, kI444, kI010, + kI210, + kI410, kNV12, }; @@ -109,6 +113,8 @@ class RTC_EXPORT VideoFrameBuffer : public rtc::RefCountInterface { const I422BufferInterface* GetI422() const; const I444BufferInterface* GetI444() const; const I010BufferInterface* GetI010() const; + const I210BufferInterface* GetI210() const; + const I410BufferInterface* GetI410() const; const NV12BufferInterface* GetNV12() const; // From a kNative frame, returns a VideoFrameBuffer with a pixel format in @@ -218,7 +224,8 @@ class I444BufferInterface : public PlanarYuv8Buffer { ~I444BufferInterface() override {} }; -// This interface represents 8-bit to 16-bit color depth formats: Type::kI010. +// This interface represents 8-bit to 16-bit color depth formats: Type::kI010 or +// Type::kI210 . class PlanarYuv16BBuffer : public PlanarYuvBuffer { public: // Returns pointer to the pixel data for a given plane. The memory is owned by @@ -244,6 +251,32 @@ class I010BufferInterface : public PlanarYuv16BBuffer { ~I010BufferInterface() override {} }; +// Represents Type::kI210, allocates 16 bits per pixel and fills 10 least +// significant bits with color information. +class I210BufferInterface : public PlanarYuv16BBuffer { + public: + Type type() const override; + + int ChromaWidth() const final; + int ChromaHeight() const final; + + protected: + ~I210BufferInterface() override {} +}; + +// Represents Type::kI410, allocates 16 bits per pixel and fills 10 least +// significant bits with color information. +class I410BufferInterface : public PlanarYuv16BBuffer { + public: + Type type() const override; + + int ChromaWidth() const final; + int ChromaHeight() const final; + + protected: + ~I410BufferInterface() override {} +}; + class BiplanarYuvBuffer : public VideoFrameBuffer { public: virtual int ChromaWidth() const = 0; diff --git a/api/video/video_frame_metadata.cc b/api/video/video_frame_metadata.cc index df82875eb9..df33cf5566 100644 --- a/api/video/video_frame_metadata.cc +++ b/api/video/video_frame_metadata.cc @@ -10,19 +10,146 @@ #include "api/video/video_frame_metadata.h" -#include "modules/rtp_rtcp/source/rtp_video_header.h" +#include namespace webrtc { -VideoFrameMetadata::VideoFrameMetadata(const RTPVideoHeader& header) - : width_(header.width), height_(header.height) { - if (header.generic) { - frame_id_ = header.generic->frame_id; - spatial_index_ = header.generic->spatial_index; - temporal_index_ = header.generic->temporal_index; - frame_dependencies_ = header.generic->dependencies; - decode_target_indications_ = header.generic->decode_target_indications; - } +VideoFrameMetadata::VideoFrameMetadata() = default; + +VideoFrameType VideoFrameMetadata::GetFrameType() const { + return frame_type_; +} + +void VideoFrameMetadata::SetFrameType(VideoFrameType frame_type) { + frame_type_ = frame_type; +} + +uint16_t VideoFrameMetadata::GetWidth() const { + return width_; +} + +void VideoFrameMetadata::SetWidth(uint16_t width) { + width_ = width; +} + +uint16_t VideoFrameMetadata::GetHeight() const { + return height_; +} + +void VideoFrameMetadata::SetHeight(uint16_t height) { + height_ = height; +} + +VideoRotation VideoFrameMetadata::GetRotation() const { + return rotation_; +} + +void VideoFrameMetadata::SetRotation(VideoRotation rotation) { + rotation_ = rotation; +} + +VideoContentType VideoFrameMetadata::GetContentType() const { + return content_type_; +} + +void VideoFrameMetadata::SetContentType(VideoContentType content_type) { + content_type_ = content_type; +} + +absl::optional VideoFrameMetadata::GetFrameId() const { + return frame_id_; +} + +void VideoFrameMetadata::SetFrameId(absl::optional frame_id) { + frame_id_ = frame_id; +} + +int VideoFrameMetadata::GetSpatialIndex() const { + return spatial_index_; +} + +void VideoFrameMetadata::SetSpatialIndex(int spatial_index) { + spatial_index_ = spatial_index; +} + +int VideoFrameMetadata::GetTemporalIndex() const { + return temporal_index_; +} + +void VideoFrameMetadata::SetTemporalIndex(int temporal_index) { + temporal_index_ = temporal_index; +} + +rtc::ArrayView VideoFrameMetadata::GetFrameDependencies() const { + return frame_dependencies_; +} + +void VideoFrameMetadata::SetFrameDependencies( + rtc::ArrayView frame_dependencies) { + frame_dependencies_.assign(frame_dependencies.begin(), + frame_dependencies.end()); +} + +rtc::ArrayView +VideoFrameMetadata::GetDecodeTargetIndications() const { + return decode_target_indications_; +} + +void VideoFrameMetadata::SetDecodeTargetIndications( + rtc::ArrayView decode_target_indications) { + decode_target_indications_.assign(decode_target_indications.begin(), + decode_target_indications.end()); +} + +bool VideoFrameMetadata::GetIsLastFrameInPicture() const { + return is_last_frame_in_picture_; +} + +void VideoFrameMetadata::SetIsLastFrameInPicture( + bool is_last_frame_in_picture) { + is_last_frame_in_picture_ = is_last_frame_in_picture; +} + +uint8_t VideoFrameMetadata::GetSimulcastIdx() const { + return simulcast_idx_; +} + +void VideoFrameMetadata::SetSimulcastIdx(uint8_t simulcast_idx) { + simulcast_idx_ = simulcast_idx; +} + +VideoCodecType VideoFrameMetadata::GetCodec() const { + return codec_; +} + +void VideoFrameMetadata::SetCodec(VideoCodecType codec) { + codec_ = codec; +} + +const RTPVideoHeaderCodecSpecifics& +VideoFrameMetadata::GetRTPVideoHeaderCodecSpecifics() const { + return codec_specifics_; +} + +void VideoFrameMetadata::SetRTPVideoHeaderCodecSpecifics( + RTPVideoHeaderCodecSpecifics codec_specifics) { + codec_specifics_ = std::move(codec_specifics); +} + +uint32_t VideoFrameMetadata::GetSsrc() const { + return ssrc_; +} + +void VideoFrameMetadata::SetSsrc(uint32_t ssrc) { + ssrc_ = ssrc; +} + +std::vector VideoFrameMetadata::GetCsrcs() const { + return csrcs_; +} + +void VideoFrameMetadata::SetCsrcs(std::vector csrcs) { + csrcs_ = std::move(csrcs); } } // namespace webrtc diff --git a/api/video/video_frame_metadata.h b/api/video/video_frame_metadata.h index 2e9309841b..5a1c0edde7 100644 --- a/api/video/video_frame_metadata.h +++ b/api/video/video_frame_metadata.h @@ -12,47 +12,110 @@ #define API_VIDEO_VIDEO_FRAME_METADATA_H_ #include +#include #include "absl/container/inlined_vector.h" #include "absl/types/optional.h" +#include "absl/types/variant.h" #include "api/array_view.h" #include "api/transport/rtp/dependency_descriptor.h" +#include "api/video/video_codec_type.h" +#include "api/video/video_content_type.h" +#include "api/video/video_frame_type.h" +#include "api/video/video_rotation.h" +#include "modules/video_coding/codecs/h264/include/h264_globals.h" +#include "modules/video_coding/codecs/vp8/include/vp8_globals.h" +#include "modules/video_coding/codecs/vp9/include/vp9_globals.h" +#include "rtc_base/system/rtc_export.h" namespace webrtc { -struct RTPVideoHeader; +using RTPVideoHeaderCodecSpecifics = absl::variant; // A subset of metadata from the RTP video header, exposed in insertable streams // API. -class VideoFrameMetadata { +class RTC_EXPORT VideoFrameMetadata { public: - explicit VideoFrameMetadata(const RTPVideoHeader& header); + VideoFrameMetadata(); VideoFrameMetadata(const VideoFrameMetadata&) = default; VideoFrameMetadata& operator=(const VideoFrameMetadata&) = default; - uint16_t GetWidth() const { return width_; } - uint16_t GetHeight() const { return height_; } - absl::optional GetFrameId() const { return frame_id_; } - int GetSpatialIndex() const { return spatial_index_; } - int GetTemporalIndex() const { return temporal_index_; } + VideoFrameType GetFrameType() const; + void SetFrameType(VideoFrameType frame_type); - rtc::ArrayView GetFrameDependencies() const { - return frame_dependencies_; - } + uint16_t GetWidth() const; + void SetWidth(uint16_t width); + + uint16_t GetHeight() const; + void SetHeight(uint16_t height); + + VideoRotation GetRotation() const; + void SetRotation(VideoRotation rotation); + + VideoContentType GetContentType() const; + void SetContentType(VideoContentType content_type); + + absl::optional GetFrameId() const; + void SetFrameId(absl::optional frame_id); + + int GetSpatialIndex() const; + void SetSpatialIndex(int spatial_index); + + int GetTemporalIndex() const; + void SetTemporalIndex(int temporal_index); + + rtc::ArrayView GetFrameDependencies() const; + void SetFrameDependencies(rtc::ArrayView frame_dependencies); rtc::ArrayView GetDecodeTargetIndications() - const { - return decode_target_indications_; - } + const; + void SetDecodeTargetIndications( + rtc::ArrayView decode_target_indications); + + bool GetIsLastFrameInPicture() const; + void SetIsLastFrameInPicture(bool is_last_frame_in_picture); + + uint8_t GetSimulcastIdx() const; + void SetSimulcastIdx(uint8_t simulcast_idx); + + VideoCodecType GetCodec() const; + void SetCodec(VideoCodecType codec); + + // Which varient is used depends on the VideoCodecType from GetCodecs(). + const RTPVideoHeaderCodecSpecifics& GetRTPVideoHeaderCodecSpecifics() const; + void SetRTPVideoHeaderCodecSpecifics( + RTPVideoHeaderCodecSpecifics codec_specifics); + + uint32_t GetSsrc() const; + void SetSsrc(uint32_t ssrc); + std::vector GetCsrcs() const; + void SetCsrcs(std::vector csrcs); private: - int16_t width_; - int16_t height_; + VideoFrameType frame_type_ = VideoFrameType::kEmptyFrame; + int16_t width_ = 0; + int16_t height_ = 0; + VideoRotation rotation_ = VideoRotation::kVideoRotation_0; + VideoContentType content_type_ = VideoContentType::UNSPECIFIED; + + // Corresponding to GenericDescriptorInfo. absl::optional frame_id_; int spatial_index_ = 0; int temporal_index_ = 0; absl::InlinedVector frame_dependencies_; absl::InlinedVector decode_target_indications_; + + bool is_last_frame_in_picture_ = true; + uint8_t simulcast_idx_ = 0; + VideoCodecType codec_ = VideoCodecType::kVideoCodecGeneric; + RTPVideoHeaderCodecSpecifics codec_specifics_; + + // RTP info. + uint32_t ssrc_; + std::vector csrcs_; }; } // namespace webrtc diff --git a/api/video/video_frame_metadata_unittest.cc b/api/video/video_frame_metadata_unittest.cc deleted file mode 100644 index 7a808e1ea9..0000000000 --- a/api/video/video_frame_metadata_unittest.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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/video/video_frame_metadata.h" - -#include "modules/rtp_rtcp/source/rtp_video_header.h" -#include "test/gmock.h" -#include "test/gtest.h" - -namespace webrtc { -namespace { - -using ::testing::ElementsAre; -using ::testing::IsEmpty; - -TEST(VideoFrameMetadata, GetWidthReturnsCorrectValue) { - RTPVideoHeader video_header; - video_header.width = 1280u; - VideoFrameMetadata metadata(video_header); - EXPECT_EQ(metadata.GetWidth(), video_header.width); -} - -TEST(VideoFrameMetadata, GetHeightReturnsCorrectValue) { - RTPVideoHeader video_header; - video_header.height = 720u; - VideoFrameMetadata metadata(video_header); - EXPECT_EQ(metadata.GetHeight(), video_header.height); -} - -TEST(VideoFrameMetadata, GetFrameIdReturnsCorrectValue) { - RTPVideoHeader video_header; - RTPVideoHeader::GenericDescriptorInfo& generic = - video_header.generic.emplace(); - generic.frame_id = 10; - VideoFrameMetadata metadata(video_header); - EXPECT_EQ(metadata.GetFrameId().value(), 10); -} - -TEST(VideoFrameMetadata, HasNoFrameIdForHeaderWithoutGeneric) { - RTPVideoHeader video_header; - VideoFrameMetadata metadata(video_header); - ASSERT_FALSE(video_header.generic); - EXPECT_EQ(metadata.GetFrameId(), absl::nullopt); -} - -TEST(VideoFrameMetadata, GetSpatialIndexReturnsCorrectValue) { - RTPVideoHeader video_header; - RTPVideoHeader::GenericDescriptorInfo& generic = - video_header.generic.emplace(); - generic.spatial_index = 2; - VideoFrameMetadata metadata(video_header); - EXPECT_EQ(metadata.GetSpatialIndex(), 2); -} - -TEST(VideoFrameMetadata, SpatialIndexIsZeroForHeaderWithoutGeneric) { - RTPVideoHeader video_header; - VideoFrameMetadata metadata(video_header); - ASSERT_FALSE(video_header.generic); - EXPECT_EQ(metadata.GetSpatialIndex(), 0); -} - -TEST(VideoFrameMetadata, GetTemporalIndexReturnsCorrectValue) { - RTPVideoHeader video_header; - RTPVideoHeader::GenericDescriptorInfo& generic = - video_header.generic.emplace(); - generic.temporal_index = 3; - VideoFrameMetadata metadata(video_header); - EXPECT_EQ(metadata.GetTemporalIndex(), 3); -} - -TEST(VideoFrameMetadata, TemporalIndexIsZeroForHeaderWithoutGeneric) { - RTPVideoHeader video_header; - VideoFrameMetadata metadata(video_header); - ASSERT_FALSE(video_header.generic); - EXPECT_EQ(metadata.GetTemporalIndex(), 0); -} - -TEST(VideoFrameMetadata, GetFrameDependenciesReturnsCorrectValue) { - RTPVideoHeader video_header; - RTPVideoHeader::GenericDescriptorInfo& generic = - video_header.generic.emplace(); - generic.dependencies = {5, 6, 7}; - VideoFrameMetadata metadata(video_header); - EXPECT_THAT(metadata.GetFrameDependencies(), ElementsAre(5, 6, 7)); -} - -TEST(VideoFrameMetadata, FrameDependencyVectorIsEmptyForHeaderWithoutGeneric) { - RTPVideoHeader video_header; - VideoFrameMetadata metadata(video_header); - ASSERT_FALSE(video_header.generic); - EXPECT_THAT(metadata.GetFrameDependencies(), IsEmpty()); -} - -TEST(VideoFrameMetadata, GetDecodeTargetIndicationsReturnsCorrectValue) { - RTPVideoHeader video_header; - RTPVideoHeader::GenericDescriptorInfo& generic = - video_header.generic.emplace(); - generic.decode_target_indications = {DecodeTargetIndication::kSwitch}; - VideoFrameMetadata metadata(video_header); - EXPECT_THAT(metadata.GetDecodeTargetIndications(), - ElementsAre(DecodeTargetIndication::kSwitch)); -} - -TEST(VideoFrameMetadata, - DecodeTargetIndicationsVectorIsEmptyForHeaderWithoutGeneric) { - RTPVideoHeader video_header; - VideoFrameMetadata metadata(video_header); - ASSERT_FALSE(video_header.generic); - EXPECT_THAT(metadata.GetDecodeTargetIndications(), IsEmpty()); -} - -} // namespace -} // namespace webrtc diff --git a/api/video/video_source_interface.h b/api/video/video_source_interface.h index 5eb4ebfd75..38d0041718 100644 --- a/api/video/video_source_interface.h +++ b/api/video/video_source_interface.h @@ -80,6 +80,24 @@ struct RTC_EXPORT VideoSinkWants { // Note that the `resolutions` can change while frames are in flight and // should only be used as a hint when constructing the webrtc::VideoFrame. std::vector resolutions; + + // This is the resolution requested by the user using RtpEncodingParameters. + absl::optional requested_resolution; + + // `active` : is (any) of the layers/sink(s) active. + bool is_active = true; + + // This sub-struct contains information computed by VideoBroadcaster + // that aggregates several VideoSinkWants (and sends them to + // AdaptedVideoTrackSource). + struct Aggregates { + // `active_without_requested_resolution` is set by VideoBroadcaster + // when aggregating sink wants if there exists any sink (encoder) that is + // active but has not set the `requested_resolution`, i.e is relying on + // OnOutputFormatRequest to handle encode resolution. + bool any_active_without_requested_resolution = false; + }; + absl::optional aggregates; }; inline bool operator==(const VideoSinkWants::FrameSize& a, @@ -87,6 +105,11 @@ inline bool operator==(const VideoSinkWants::FrameSize& a, return a.width == b.width && a.height == b.height; } +inline bool operator!=(const VideoSinkWants::FrameSize& a, + const VideoSinkWants::FrameSize& b) { + return !(a == b); +} + template class VideoSourceInterface { public: diff --git a/api/video/video_stream_decoder.h b/api/video/video_stream_decoder.h deleted file mode 100644 index 8d71dd300c..0000000000 --- a/api/video/video_stream_decoder.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_VIDEO_VIDEO_STREAM_DECODER_H_ -#define API_VIDEO_VIDEO_STREAM_DECODER_H_ - -#include -#include -#include - -#include "api/units/time_delta.h" -#include "api/video/encoded_frame.h" -#include "api/video/video_content_type.h" -#include "api/video/video_frame.h" -#include "api/video_codecs/sdp_video_format.h" -#include "api/video_codecs/video_decoder_factory.h" - -namespace webrtc { -// NOTE: This class is still under development and may change without notice. -class VideoStreamDecoderInterface { - public: - class Callbacks { - public: - virtual ~Callbacks() = default; - - struct FrameInfo { - absl::optional qp; - VideoContentType content_type; - }; - - // Called when the VideoStreamDecoder enters a non-decodable state. - virtual void OnNonDecodableState() = 0; - - virtual void OnContinuousUntil(int64_t frame_id) {} - - virtual void OnDecodedFrame(VideoFrame frame, - const FrameInfo& frame_info) = 0; - }; - - virtual ~VideoStreamDecoderInterface() = default; - - virtual void OnFrame(std::unique_ptr frame) = 0; - - virtual void SetMinPlayoutDelay(TimeDelta min_delay) = 0; - virtual void SetMaxPlayoutDelay(TimeDelta max_delay) = 0; -}; - -} // namespace webrtc - -#endif // API_VIDEO_VIDEO_STREAM_DECODER_H_ diff --git a/api/video/video_stream_decoder_create.cc b/api/video/video_stream_decoder_create.cc deleted file mode 100644 index e14c3bc851..0000000000 --- a/api/video/video_stream_decoder_create.cc +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "api/video/video_stream_decoder_create.h" - -#include - -#include "video/video_stream_decoder_impl.h" - -namespace webrtc { - -std::unique_ptr CreateVideoStreamDecoder( - VideoStreamDecoderInterface::Callbacks* callbacks, - VideoDecoderFactory* decoder_factory, - TaskQueueFactory* task_queue_factory, - 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 deleted file mode 100644 index 974fd804ce..0000000000 --- a/api/video/video_stream_decoder_create.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef API_VIDEO_VIDEO_STREAM_DECODER_CREATE_H_ -#define API_VIDEO_VIDEO_STREAM_DECODER_CREATE_H_ - -#include -#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" - -namespace webrtc { -// The `decoder_settings` parameter is a map between: -// --> <